diff --git a/.changeset/patch-migrate-safe-output-handlers.md b/.changeset/patch-migrate-safe-output-handlers.md new file mode 100644 index 0000000000..1d9c67d701 --- /dev/null +++ b/.changeset/patch-migrate-safe-output-handlers.md @@ -0,0 +1,12 @@ +--- +"gh-aw": patch +--- + +Migrate safe output handlers to a centralized handler config object and remove handler-specific environment variables. + +All eight safe-output handlers (create_issue, add_comment, create_discussion, close_issue, close_discussion, add_labels, update_issue, update_discussion) were refactored to accept a single handler config object instead of reading many individual environment variables. This reduces the number of handler-specific env vars from 30+ down to 3 global env vars and centralizes configuration for easier testing and maintenance. + +Files changed: multiple JavaScript safe output handlers under `actions/setup/js/` and related Go compiler cleanup in `pkg/workflow/`. + +Benefits: explicit data flow, fewer environment variables, testable handlers, and simpler configuration. + diff --git a/.changeset/patch-safe-output-handler-manager.md b/.changeset/patch-safe-output-handler-manager.md new file mode 100644 index 0000000000..fe8bf081b5 --- /dev/null +++ b/.changeset/patch-safe-output-handler-manager.md @@ -0,0 +1,10 @@ +--- +"gh-aw": patch +--- + +Refactor safe outputs into a centralized handler manager using a +factory pattern. Introduces a safe output handler manager and begins +refactoring individual handlers to the factory-based interface. This is +an internal refactor (WIP) that reorganizes handler initialization and +message dispatching; tests and workflow recompilation are still pending. + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c8ff4249f4..da5e377b59 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,14 +3,7 @@ "image": "mcr.microsoft.com/devcontainers/go:1-bookworm", "customizations": { "vscode": { - "extensions": [ - "golang.go", - "GitHub.copilot-chat", - "GitHub.copilot", - "github.vscode-github-actions", - "astro-build.astro-vscode", - "DavidAnson.vscode-markdownlint" - ] + "extensions": ["golang.go", "GitHub.copilot-chat", "GitHub.copilot", "github.vscode-github-actions", "astro-build.astro-vscode", "DavidAnson.vscode-markdownlint"] }, "codespaces": { "repositories": { diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index 186d33f083..c26a48e8be 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -1994,13 +1994,8 @@ jobs: GH_AW_WORKFLOW_ID: "agent-performance-analyzer" GH_AW_WORKFLOW_NAME: "Agent Performance Analyzer - Meta-Orchestrator" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2023,48 +2018,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10},\"create_discussion\":{\"max\":2},\"create_issue\":{\"max\":5}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - GH_AW_CREATED_DISCUSSION_URL: ${{ steps.create_discussion.outputs.discussion_url }} - GH_AW_CREATED_DISCUSSION_NUMBER: ${{ steps.create_discussion.outputs.discussion_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index 5ab7046787..3cfe93bd80 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -1199,7 +1199,8 @@ jobs: GH_AW_WORKFLOW_ID: "ai-moderator" GH_AW_WORKFLOW_NAME: "AI Moderator" outputs: - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1222,12 +1223,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"spam\",\"ai-generated\",\"link-spam\",\"ai-inspected\"],\"target\":\"*\"}}" GH_AW_LABELS_ALLOWED: "spam,ai-generated,link-spam,ai-inspected" GH_AW_LABELS_TARGET: "*" with: @@ -1235,7 +1236,7 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Hide Comment id: hide_comment diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index ea6e9d0aba..a8a8847142 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1530,8 +1530,8 @@ jobs: GH_AW_WORKFLOW_ID: "archie" GH_AW_WORKFLOW_NAME: "Archie" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1554,17 +1554,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index b2505b53a9..5bad9a061e 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -1315,8 +1315,8 @@ jobs: GH_AW_WORKFLOW_ID: "artifacts-summary" GH_AW_WORKFLOW_NAME: "Artifacts Summary" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1350,18 +1350,18 @@ jobs: github-api-url: ${{ github.api_url }} permission-contents: read permission-discussions: write - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"artifacts\",\"max\":1}}" with: github-token: ${{ steps.app-token.outputs.token }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Invalidate GitHub App token if: always() && steps.app-token.outputs.token != '' diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index e691a6d583..010345badb 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1768,8 +1768,8 @@ jobs: GH_AW_WORKFLOW_ID: "audit-workflows" GH_AW_WORKFLOW_NAME: "Agentic Workflow Audit Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1792,18 +1792,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index c0babb3f05..6cc8cfa51e 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -1594,8 +1594,8 @@ jobs: GH_AW_WORKFLOW_ID: "blog-auditor" GH_AW_WORKFLOW_NAME: "Blog Auditor" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1618,17 +1618,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"Audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index f6fafcd7b9..9794229b95 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1409,8 +1409,8 @@ jobs: GH_AW_WORKFLOW_ID: "brave" GH_AW_WORKFLOW_NAME: "Brave Web Search Agent" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1433,17 +1433,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index 68be6b0f14..0b40533c77 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -1459,9 +1459,8 @@ jobs: GH_AW_WORKFLOW_ID: "breaking-change-checker" GH_AW_WORKFLOW_NAME: "Breaking Change Checker" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1484,12 +1483,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[breaking-change] " GH_AW_ISSUE_LABELS: "breaking-change,automated-analysis" with: @@ -1497,6 +1496,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/campaign-generator.lock.yml b/.github/workflows/campaign-generator.lock.yml index 3719e597f2..0edc25d3fc 100644 --- a/.github/workflows/campaign-generator.lock.yml +++ b/.github/workflows/campaign-generator.lock.yml @@ -1346,6 +1346,8 @@ jobs: GH_AW_WORKFLOW_NAME: "Campaign Generator" outputs: assign_to_agent_assigned: ${{ steps.assign_to_agent.outputs.assigned }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1368,30 +1370,30 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Assign To Agent - id: assign_to_agent - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"update_issue\":{\"allow_body\":true,\"allow_status\":true,\"max\":1,\"target\":\"${{ github.event.issue.number }}\"}}" with: - github-token: ${{ secrets.GH_AW_AGENT_TOKEN }} + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/assign_to_agent.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - - name: Update Issue - id: update_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_issue')) + - name: Assign To Agent + id: assign_to_agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent')) uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GH_AW_AGENT_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/update_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/assign_to_agent.cjs'); await main(); diff --git a/.github/workflows/campaign-manager.lock.yml b/.github/workflows/campaign-manager.lock.yml index 3a009495c8..a2a782d9e7 100644 --- a/.github/workflows/campaign-manager.lock.yml +++ b/.github/workflows/campaign-manager.lock.yml @@ -1806,13 +1806,8 @@ jobs: GH_AW_WORKFLOW_ID: "campaign-manager" GH_AW_WORKFLOW_NAME: "Campaign Manager - Meta-Orchestrator" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1835,49 +1830,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10},\"create_discussion\":{\"max\":3},\"create_issue\":{\"max\":5}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - GH_AW_CREATED_DISCUSSION_URL: ${{ steps.create_discussion.outputs.discussion_url }} - GH_AW_CREATED_DISCUSSION_NUMBER: ${{ steps.create_discussion.outputs.discussion_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Update Project id: update_project diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 54ae025626..d23705ddd9 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1546,11 +1546,8 @@ jobs: GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/ci-doctor.md@ea350161ad5dcc9624cf510f134c6a9e39a6f94d" GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/ea350161ad5dcc9624cf510f134c6a9e39a6f94d/workflows/ci-doctor.md" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1573,35 +1570,19 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[CI Failure Doctor] " with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index 1b435d0604..1d11d804b9 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -1406,9 +1406,8 @@ jobs: GH_AW_WORKFLOW_ID: "cli-consistency-checker" GH_AW_WORKFLOW_NAME: "CLI Consistency Checker" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1431,12 +1430,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":5}}" GH_AW_ISSUE_TITLE_PREFIX: "[cli-consistency] " GH_AW_ISSUE_LABELS: "automation,cli,documentation" with: @@ -1444,6 +1443,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index a58b7ddcf9..4ffb864674 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -1659,9 +1659,8 @@ jobs: GH_AW_WORKFLOW_ID: "cli-version-checker" GH_AW_WORKFLOW_NAME: "CLI Version Checker" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1684,12 +1683,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[ca] " GH_AW_ISSUE_LABELS: "automation,dependencies" with: @@ -1697,7 +1696,7 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 7b0e92b753..402274fa8c 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1897,10 +1897,10 @@ jobs: GH_AW_WORKFLOW_ID: "cloclo" GH_AW_WORKFLOW_NAME: "/cloclo" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1948,6 +1948,19 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); - name: Create Pull Request id: create_pull_request if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) @@ -1970,21 +1983,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); update_cache_memory: needs: diff --git a/.github/workflows/close-old-discussions.lock.yml b/.github/workflows/close-old-discussions.lock.yml index 57a67036bb..23697cd4a6 100644 --- a/.github/workflows/close-old-discussions.lock.yml +++ b/.github/workflows/close-old-discussions.lock.yml @@ -1391,6 +1391,9 @@ jobs: GH_AW_ENGINE_ID: "codex" GH_AW_WORKFLOW_ID: "close-old-discussions" GH_AW_WORKFLOW_NAME: "Close Outdated Discussions" + outputs: + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1413,18 +1416,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Close Discussion - id: close_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_discussion\":{\"max\":100}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/close_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index 929e934b7c..b37da945c8 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -1510,8 +1510,8 @@ jobs: GH_AW_WORKFLOW_ID: "commit-changes-analyzer" GH_AW_WORKFLOW_NAME: "Commit Changes Analyzer" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1534,17 +1534,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"dev\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 7c075b022d..e5a8982f67 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -1992,8 +1992,8 @@ jobs: GH_AW_WORKFLOW_ID: "copilot-agent-analysis" GH_AW_WORKFLOW_NAME: "Copilot Agent PR Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2016,18 +2016,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index e12a752d75..e16d1f32ff 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -1453,8 +1453,8 @@ jobs: GH_AW_WORKFLOW_ID: "copilot-pr-merged-report" GH_AW_WORKFLOW_NAME: "Daily Copilot PR Merged Report" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1477,17 +1477,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index 20b10c7d4f..82c7c20e77 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -2228,8 +2228,8 @@ jobs: GH_AW_WORKFLOW_ID: "copilot-pr-nlp-analysis" GH_AW_WORKFLOW_NAME: "Copilot PR Conversation NLP Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2252,18 +2252,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audit\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index a295547320..0d67e52d3d 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -1733,8 +1733,8 @@ jobs: GH_AW_WORKFLOW_ID: "copilot-pr-prompt-analysis" GH_AW_WORKFLOW_NAME: "Copilot PR Prompt Pattern Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1757,18 +1757,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 876ddab0e6..14611de2b4 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -2732,8 +2732,8 @@ jobs: GH_AW_WORKFLOW_ID: "copilot-session-insights" GH_AW_WORKFLOW_NAME: "Copilot Session Insights" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2756,18 +2756,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 86c77ce977..113809dd78 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -1597,8 +1597,8 @@ jobs: GH_AW_WORKFLOW_ID: "craft" GH_AW_WORKFLOW_NAME: "Workflow Craft Agent" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} push_to_pull_request_branch_commit_url: ${{ steps.push_to_pull_request_branch.outputs.commit_url }} steps: - name: Checkout actions folder @@ -1647,18 +1647,18 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Push To Pull Request Branch id: push_to_pull_request_branch diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index 6e71844c2b..4a950c9e27 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -1222,9 +1222,9 @@ jobs: GH_AW_WORKFLOW_ID: "daily-assign-issue-to-user" GH_AW_WORKFLOW_NAME: "Auto-Assign Issue" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} assign_to_user_assigned: ${{ steps.assign_to_user.outputs.assigned }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1247,19 +1247,19 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"*\"}}" GH_AW_COMMENT_TARGET: "*" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Assign To User id: assign_to_user diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 8b9732cec2..8520b1fa8a 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -1524,8 +1524,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-code-metrics" GH_AW_WORKFLOW_NAME: "Daily Code Metrics and Trend Tracking Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1548,18 +1548,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index ced564e1ee..03fd975645 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -2329,8 +2329,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-copilot-token-report" GH_AW_WORKFLOW_NAME: "Daily Copilot Token Consumption Report" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2353,18 +2353,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index 3cf77b8080..d9e926a4fa 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -1174,8 +1174,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-fact" GH_AW_WORKFLOW_NAME: "Daily Fact About gh-aw" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts uses: githubnext/gh-aw/actions/setup@523f6cfa6283c35c866e1cb55b567f7b6cdb8ee1 @@ -1192,12 +1192,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"4750\"}}" GH_AW_COMMENT_TARGET: "4750" GITHUB_AW_COMMENT_DISCUSSION: "true" with: @@ -1205,6 +1205,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index cebeb940d0..be2cf29a3f 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -2280,9 +2280,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-file-diet" GH_AW_WORKFLOW_NAME: "Daily File Diet" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2316,12 +2315,12 @@ jobs: github-api-url: ${{ github.api_url }} permission-contents: read permission-issues: write - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[file-diet] " GH_AW_ISSUE_LABELS: "refactoring,code-health,automated-analysis" with: @@ -2329,7 +2328,7 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Invalidate GitHub App token if: always() && steps.app-token.outputs.token != '' diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 2f7becf12f..163a2352fb 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -1780,8 +1780,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-firewall-report" GH_AW_WORKFLOW_NAME: "Daily Firewall Logs Collector and Reporter" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1804,18 +1804,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index ef1c2794fb..1fb2ce3377 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -2315,8 +2315,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-issues-report" GH_AW_WORKFLOW_NAME: "Daily Issues Report Generator" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2339,31 +2339,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_discussion\":{\"max\":10},\"create_discussion\":{\"category\":\"General\",\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); - await main(); - - name: Close Discussion - id: close_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/close_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 10c7bf2214..e528b7f1ac 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -1490,9 +1490,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-multi-device-docs-tester" GH_AW_WORKFLOW_NAME: "Multi-Device Docs Tester" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1515,18 +1514,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); upload_assets: diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index d39fb2a643..455b13c410 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -2165,8 +2165,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-news" GH_AW_WORKFLOW_NAME: "Daily News" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2189,18 +2189,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"daily-news\",\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index ea82abd86e..f20f73d01e 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -2242,8 +2242,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-performance-summary" GH_AW_WORKFLOW_NAME: "Daily Project Performance Summary Generator (Using Safe Inputs)" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2266,31 +2266,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_discussion\":{\"max\":10},\"create_discussion\":{\"category\":\"General\",\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); - await main(); - - name: Close Discussion - id: close_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/close_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index 427c61ccf0..2ce6425127 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -1934,8 +1934,8 @@ jobs: GH_AW_WORKFLOW_ID: "daily-repo-chronicle" GH_AW_WORKFLOW_NAME: "The Daily Repository Chronicle" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1958,18 +1958,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"expires\":3,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index 67237463c6..12b3a48101 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -1344,9 +1344,8 @@ jobs: GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-team-status.md@d3422bf940923ef1d43db5559652b8e1e71869f3" GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/d3422bf940923ef1d43db5559652b8e1e71869f3/workflows/daily-team-status.md" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1369,12 +1368,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"expires\":3,\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[team-status] " GH_AW_ISSUE_EXPIRES: "3" with: @@ -1382,6 +1381,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index edecb983b3..50114b0b70 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -1856,8 +1856,8 @@ jobs: GH_AW_WORKFLOW_ID: "deep-report" GH_AW_WORKFLOW_NAME: "DeepReport - Intelligence Gathering Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1880,18 +1880,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"reports\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index a72a6cdd88..d571b6dc2f 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -1675,9 +1675,8 @@ jobs: GH_AW_WORKFLOW_ID: "dependabot-go-checker" GH_AW_WORKFLOW_NAME: "Dependabot Dependency Checker" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1700,12 +1699,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_issue\":{\"max\":20,\"required_title_prefix\":\"[deps]\",\"target\":\"*\"},\"create_issue\":{\"max\":10}}" GH_AW_ISSUE_TITLE_PREFIX: "[deps]" GH_AW_ISSUE_LABELS: "dependencies,go" with: @@ -1713,19 +1712,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Close Issue - id: close_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_issue')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/close_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index 75e786bf6f..42fcfecc65 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -1495,8 +1495,8 @@ jobs: GH_AW_WORKFLOW_ID: "dev-hawk" GH_AW_WORKFLOW_NAME: "Dev Hawk" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1519,18 +1519,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"*\"}}" GH_AW_COMMENT_TARGET: "*" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 0d6bde29dc..bce171b979 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1169,8 +1169,8 @@ jobs: GH_AW_WORKFLOW_ID: "dev" GH_AW_WORKFLOW_NAME: "Dev" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1193,18 +1193,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_STAGED: "true" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 84bc45f3a7..f21bd6ba62 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -2038,10 +2038,10 @@ jobs: GH_AW_WORKFLOW_ID: "developer-docs-consolidator" GH_AW_WORKFLOW_NAME: "Developer Documentation Consolidator" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2089,18 +2089,18 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"General\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create Pull Request id: create_pull_request diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index 36c3f65338..4c1225d060 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -1426,8 +1426,8 @@ jobs: GH_AW_WORKFLOW_ID: "docs-noob-tester" GH_AW_WORKFLOW_NAME: "Documentation Noob Tester" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1450,18 +1450,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"General\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); upload_assets: diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 456148ef96..748499ee14 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -1423,9 +1423,8 @@ jobs: GH_AW_WORKFLOW_ID: "duplicate-code-detector" GH_AW_WORKFLOW_NAME: "Duplicate Code Detector" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1448,19 +1447,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ISSUE_TITLE_PREFIX: "[duplicate-code] " - GH_AW_ISSUE_LABELS: "code-quality,automated-analysis" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"code-quality\",\"automated-analysis\"],\"max\":3,\"title_prefix\":\"[duplicate-code] \"}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 1b7ed47bbd..307fff7d27 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -1279,8 +1279,8 @@ jobs: GH_AW_WORKFLOW_ID: "example-workflow-analyzer" GH_AW_WORKFLOW_NAME: "Weekly Workflow Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1303,17 +1303,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"Audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 128afe5b23..bc15fa1633 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -1975,8 +1975,8 @@ jobs: GH_AW_WORKFLOW_ID: "github-mcp-structural-analysis" GH_AW_WORKFLOW_NAME: "GitHub MCP Structural Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1999,18 +1999,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 7b756f2060..843cfc5d9c 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -1848,10 +1848,10 @@ jobs: GH_AW_WORKFLOW_ID: "github-mcp-tools-report" GH_AW_WORKFLOW_NAME: "GitHub MCP Remote Server Tools Report Generator" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1899,18 +1899,18 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create Pull Request id: create_pull_request diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index d016f80aa2..5780e26319 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -1651,8 +1651,8 @@ jobs: GH_AW_WORKFLOW_ID: "go-fan" GH_AW_WORKFLOW_NAME: "Go Fan" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1675,18 +1675,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"General\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml b/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml index 749bacc17a..19af2bc98e 100644 --- a/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml +++ b/.github/workflows/go-file-size-reduction-project64.campaign.g.lock.yml @@ -1527,8 +1527,8 @@ jobs: GH_AW_WORKFLOW_ID: "go-file-size-reduction-project64.campaign.g" GH_AW_WORKFLOW_NAME: "Go File Size Reduction Campaign (Project 64)" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1551,18 +1551,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Update Project id: update_project diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 4ff3a7f881..e593ba140a 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -1451,9 +1451,8 @@ jobs: GH_AW_WORKFLOW_ID: "go-pattern-detector" GH_AW_WORKFLOW_NAME: "Go Pattern Detector" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1476,12 +1475,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[ast-grep] " GH_AW_ISSUE_LABELS: "code-quality,ast-grep" with: @@ -1489,6 +1488,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 9ebd999a11..d189287760 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1524,8 +1524,8 @@ jobs: GH_AW_WORKFLOW_ID: "grumpy-reviewer" GH_AW_WORKFLOW_NAME: "Grumpy Code Reviewer 🔥" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1548,18 +1548,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create PR Review Comment id: create_pr_review_comment diff --git a/.github/workflows/human-ai-collaboration.lock.yml b/.github/workflows/human-ai-collaboration.lock.yml index 761db26965..b588657cb1 100644 --- a/.github/workflows/human-ai-collaboration.lock.yml +++ b/.github/workflows/human-ai-collaboration.lock.yml @@ -1767,9 +1767,8 @@ jobs: GH_AW_WORKFLOW_ID: "human-ai-collaboration" GH_AW_WORKFLOW_NAME: "Human-AI Collaboration Campaign" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1792,17 +1791,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/incident-response.lock.yml b/.github/workflows/incident-response.lock.yml index a401a5e318..148f3b430f 100644 --- a/.github/workflows/incident-response.lock.yml +++ b/.github/workflows/incident-response.lock.yml @@ -1928,14 +1928,10 @@ jobs: GH_AW_WORKFLOW_ID: "incident-response" GH_AW_WORKFLOW_NAME: "Campaign - Incident Response" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1983,19 +1979,19 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{},\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_LABELS: "campaign-tracker,incident" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create Pull Request id: create_pull_request @@ -2016,35 +2012,4 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); - await main(); diff --git a/.github/workflows/intelligence.lock.yml b/.github/workflows/intelligence.lock.yml index 7c2bc6aec5..2b3dc7e53e 100644 --- a/.github/workflows/intelligence.lock.yml +++ b/.github/workflows/intelligence.lock.yml @@ -2439,9 +2439,8 @@ jobs: GH_AW_WORKFLOW_ID: "intelligence" GH_AW_WORKFLOW_NAME: "Campaign Intelligence System" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2464,18 +2463,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index dcfac16d00..fc8ad69599 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -1494,11 +1494,8 @@ jobs: GH_AW_WORKFLOW_ID: "issue-arborist" GH_AW_WORKFLOW_NAME: "Issue Arborist" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1521,32 +1518,19 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"Audits\",\"max\":1},\"create_issue\":{\"max\":5}}" GH_AW_ISSUE_TITLE_PREFIX: "[Parent] " with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Link Sub Issue id: link_sub_issue diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 3e39884812..13699d0a04 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1123,7 +1123,8 @@ jobs: GH_AW_WORKFLOW_ID: "issue-classifier" GH_AW_WORKFLOW_NAME: "Issue Classifier" outputs: - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1146,12 +1147,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\"],\"max\":1}}" GH_AW_LABELS_ALLOWED: "bug,feature,enhancement,documentation" GH_AW_LABELS_MAX_COUNT: 1 with: @@ -1159,6 +1160,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 9cf580bdca..56387cf3e5 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -1458,9 +1458,9 @@ jobs: GH_AW_WORKFLOW_ID: "issue-monster" GH_AW_WORKFLOW_NAME: "Issue Monster" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} assign_to_agent_assigned: ${{ steps.assign_to_agent.outputs.assigned }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1483,18 +1483,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Assign To Agent id: assign_to_agent diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index 792004bc9c..29a4348cc3 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -1202,9 +1202,8 @@ jobs: GH_AW_WORKFLOW_ID: "issue-triage-agent" GH_AW_WORKFLOW_NAME: "Issue Triage Agent" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1227,31 +1226,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\",\"question\",\"help-wanted\",\"good-first-issue\"]}}" GH_AW_LABELS_ALLOWED: "bug,feature,enhancement,documentation,question,help-wanted,good-first-issue" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index f2c73d8f6c..f380d27458 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -1641,8 +1641,8 @@ jobs: GH_AW_WORKFLOW_ID: "lockfile-stats" GH_AW_WORKFLOW_NAME: "Lockfile Statistics Analysis Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1665,18 +1665,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index d09dc00ef0..0b2cc617d6 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -2076,8 +2076,8 @@ jobs: GH_AW_WORKFLOW_ID: "mcp-inspector" GH_AW_WORKFLOW_NAME: "MCP Inspector Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2100,18 +2100,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 5c1b71620f..38025d8d5e 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -2079,8 +2079,8 @@ jobs: GH_AW_WORKFLOW_ID: "org-health-report" GH_AW_WORKFLOW_NAME: "Organization Health Report" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2103,18 +2103,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"reports\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/org-wide-rollout.lock.yml b/.github/workflows/org-wide-rollout.lock.yml index 41492a3528..ade56055ef 100644 --- a/.github/workflows/org-wide-rollout.lock.yml +++ b/.github/workflows/org-wide-rollout.lock.yml @@ -1956,14 +1956,10 @@ jobs: GH_AW_WORKFLOW_ID: "org-wide-rollout" GH_AW_WORKFLOW_NAME: "Campaign - Org-Wide Rollout" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2011,19 +2007,19 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{},\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_LABELS: "campaign-tracker,org-rollout" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create Pull Request id: create_pull_request @@ -2044,35 +2040,4 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); - await main(); diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 6e6cc3b9bb..a93dab8fae 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1516,8 +1516,8 @@ jobs: GH_AW_WORKFLOW_ID: "pdf-summary" GH_AW_WORKFLOW_NAME: "Resource Summarizer Agent" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1540,18 +1540,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 7d48ee3962..f6ed2b7e35 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1550,9 +1550,8 @@ jobs: GH_AW_WORKFLOW_ID: "plan" GH_AW_WORKFLOW_NAME: "Plan Command" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1575,12 +1574,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_discussion\":{\"max\":1,\"required_category\":\"Ideas\"},\"create_issue\":{\"max\":6}}" GH_AW_ISSUE_TITLE_PREFIX: "[plan] " GH_AW_ISSUE_LABELS: "plan,ai-generated" with: @@ -1588,19 +1587,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Close Discussion - id: close_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/close_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 22ce7d18ba..4824573003 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1965,18 +1965,12 @@ jobs: GH_AW_WORKFLOW_ID: "poem-bot" GH_AW_WORKFLOW_NAME: "Poem Bot - A Creative Agentic Workflow" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} create_agent_task_task_number: ${{ steps.create_agent_task.outputs.task_number }} create_agent_task_task_url: ${{ steps.create_agent_task.outputs.task_url }} - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} push_to_pull_request_branch_commit_url: ${{ steps.push_to_pull_request_branch.outputs.commit_url }} steps: - name: Checkout actions folder @@ -2025,35 +2019,24 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"add_labels\":{\"allowed\":[\"poetry\",\"creative\",\"automation\",\"ai-generated\",\"epic\",\"haiku\",\"sonnet\",\"limerick\"],\"max\":5},\"create_discussion\":{\"category\":\"General\",\"max\":2},\"create_issue\":{\"max\":2},\"update_issue\":{\"allow_body\":true,\"allow_status\":true,\"allow_title\":true,\"max\":2,\"target\":\"*\"}}" GH_AW_ISSUE_TITLE_PREFIX: "[🎭 POEM-BOT] " GH_AW_ISSUE_LABELS: "poetry,automation,ai-generated" GH_AW_SAFE_OUTPUTS_STAGED: "true" + GH_AW_COMMENT_TARGET: "*" + GH_AW_LABELS_ALLOWED: "poetry,creative,automation,ai-generated,epic,haiku,sonnet,limerick" + GH_AW_LABELS_MAX_COUNT: 5 with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_STAGED: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create Pull Request id: create_pull_request @@ -2078,28 +2061,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_COMMENT_TARGET: "*" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - GH_AW_CREATED_DISCUSSION_URL: ${{ steps.create_discussion.outputs.discussion_url }} - GH_AW_CREATED_DISCUSSION_NUMBER: ${{ steps.create_discussion.outputs.discussion_number }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - GH_AW_SAFE_OUTPUTS_STAGED: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - name: Close Pull Request id: close_pull_request if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_pull_request')) @@ -2129,36 +2090,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pr_review_comment.cjs'); await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_LABELS_ALLOWED: "poetry,creative,automation,ai-generated,epic,haiku,sonnet,limerick" - GH_AW_LABELS_MAX_COUNT: 5 - GH_AW_SAFE_OUTPUTS_STAGED: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); - await main(); - - name: Update Issue - id: update_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_issue')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_STAGED: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/update_issue.cjs'); - await main(); - name: Push To Pull Request Branch id: push_to_pull_request_branch if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index 545374bda6..1dc5bba68d 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -2039,8 +2039,8 @@ jobs: GH_AW_WORKFLOW_ID: "portfolio-analyst" GH_AW_WORKFLOW_NAME: "Automated Portfolio Analyst" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2063,18 +2063,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"Audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index 63f8f0c8eb..f626b50efe 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1845,10 +1845,8 @@ jobs: GH_AW_WORKFLOW_ID: "pr-nitpick-reviewer" GH_AW_WORKFLOW_NAME: "PR Nitpick Reviewer 🔍" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1871,33 +1869,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3},\"create_discussion\":{\"category\":\"General\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_DISCUSSION_URL: ${{ steps.create_discussion.outputs.discussion_url }} - GH_AW_CREATED_DISCUSSION_NUMBER: ${{ steps.create_discussion.outputs.discussion_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Create PR Review Comment id: create_pr_review_comment diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 8c5fdadff1..3c199903e4 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -2061,8 +2061,8 @@ jobs: GH_AW_WORKFLOW_ID: "prompt-clustering-analysis" GH_AW_WORKFLOW_NAME: "Copilot Agent Prompt Clustering Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2085,18 +2085,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 66de4c3052..0d847ddaf8 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -2336,8 +2336,8 @@ jobs: GH_AW_WORKFLOW_ID: "python-data-charts" GH_AW_WORKFLOW_NAME: "Python Data Visualization Generator" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2360,18 +2360,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"artifacts\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 2b35b75542..78855a674d 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1886,10 +1886,10 @@ jobs: GH_AW_WORKFLOW_ID: "q" GH_AW_WORKFLOW_NAME: "Q" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1937,6 +1937,19 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); - name: Create Pull Request id: create_pull_request if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) @@ -1959,21 +1972,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); update_cache_memory: needs: diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 40af8f2ea1..1a6ba20600 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -1312,8 +1312,8 @@ jobs: GH_AW_WORKFLOW_ID: "repo-tree-map" GH_AW_WORKFLOW_NAME: "Repository Tree Map Generator" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1336,17 +1336,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"dev\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index d5f16b187c..02de8e8588 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -1828,8 +1828,8 @@ jobs: GH_AW_WORKFLOW_ID: "repository-quality-improver" GH_AW_WORKFLOW_NAME: "Repository Quality Improvement Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1852,18 +1852,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"general\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index 5e0d37ea05..6f3550ddea 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -1259,8 +1259,8 @@ jobs: GH_AW_WORKFLOW_ID: "research" GH_AW_WORKFLOW_NAME: "Basic Research Agent" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1283,17 +1283,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"research\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index b0f28623a4..db6372c41d 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -1766,8 +1766,8 @@ jobs: GH_AW_WORKFLOW_ID: "safe-output-health" GH_AW_WORKFLOW_NAME: "Safe Output Health Monitor" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1790,18 +1790,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index c914018223..34cb3bb409 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -1597,8 +1597,8 @@ jobs: GH_AW_WORKFLOW_ID: "schema-consistency-checker" GH_AW_WORKFLOW_NAME: "Schema Consistency Checker" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1621,18 +1621,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 11b6ddb395..9450894b2b 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1794,8 +1794,8 @@ jobs: GH_AW_WORKFLOW_ID: "scout" GH_AW_WORKFLOW_NAME: "Scout" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1818,18 +1818,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml index 8d24508b73..8234d24491 100644 --- a/.github/workflows/security-compliance.lock.yml +++ b/.github/workflows/security-compliance.lock.yml @@ -1587,9 +1587,8 @@ jobs: GH_AW_WORKFLOW_ID: "security-compliance" GH_AW_WORKFLOW_NAME: "Security Compliance Campaign" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1612,18 +1611,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":100}}" GH_AW_ISSUE_LABELS: "security,campaign-tracker" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index ea4254707d..b6a6ab531e 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -1781,9 +1781,8 @@ jobs: GH_AW_WORKFLOW_ID: "semantic-function-refactor" GH_AW_WORKFLOW_NAME: "Semantic Function Refactoring" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1806,12 +1805,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"close_issue\":{\"max\":10,\"required_title_prefix\":\"[refactor] \",\"target\":\"*\"},\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[refactor] " GH_AW_ISSUE_LABELS: "refactoring,code-quality,automated-analysis" with: @@ -1819,19 +1818,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Close Issue - id: close_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'close_issue')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/close_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 45c7a16381..c6469d33f5 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -1676,12 +1676,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-claude" GH_AW_WORKFLOW_NAME: "Smoke Claude" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1704,50 +1700,21 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-claude\"]},\"create_issue\":{\"expires\":1,\"max\":1}}" GH_AW_ISSUE_EXPIRES: "1" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_HIDE_OLDER_COMMENTS: "true" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-claude" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/smoke-codex-firewall.lock.yml b/.github/workflows/smoke-codex-firewall.lock.yml index 01cb18d9ce..9543fdcaa7 100644 --- a/.github/workflows/smoke-codex-firewall.lock.yml +++ b/.github/workflows/smoke-codex-firewall.lock.yml @@ -1357,12 +1357,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-codex-firewall" GH_AW_WORKFLOW_NAME: "Smoke Codex Firewall" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1385,50 +1381,21 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-codex-firewall\"]},\"create_issue\":{\"expires\":1,\"max\":1}}" GH_AW_ISSUE_EXPIRES: "1" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_HIDE_OLDER_COMMENTS: "true" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-codex-firewall" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Hide Comment id: hide_comment diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 004bec5724..682945dc19 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1468,12 +1468,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-codex" GH_AW_WORKFLOW_NAME: "Smoke Codex" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1496,50 +1492,21 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-codex\"]},\"create_issue\":{\"expires\":1,\"max\":1}}" GH_AW_ISSUE_EXPIRES: "1" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_HIDE_OLDER_COMMENTS: "true" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-codex" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Hide Comment id: hide_comment diff --git a/.github/workflows/smoke-copilot-no-firewall.lock.yml b/.github/workflows/smoke-copilot-no-firewall.lock.yml index edf977c0a2..7549bd87bd 100644 --- a/.github/workflows/smoke-copilot-no-firewall.lock.yml +++ b/.github/workflows/smoke-copilot-no-firewall.lock.yml @@ -1474,9 +1474,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-copilot-no-firewall" GH_AW_WORKFLOW_NAME: "Smoke Copilot No Firewall" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1499,32 +1498,19 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-copilot-no-firewall\"]}}" GH_AW_HIDE_OLDER_COMMENTS: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-copilot-no-firewall" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/smoke-copilot-playwright.lock.yml b/.github/workflows/smoke-copilot-playwright.lock.yml index ff90603149..0fc2023c7d 100644 --- a/.github/workflows/smoke-copilot-playwright.lock.yml +++ b/.github/workflows/smoke-copilot-playwright.lock.yml @@ -1621,12 +1621,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-copilot-playwright" GH_AW_WORKFLOW_NAME: "Smoke Copilot Playwright" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1649,50 +1645,21 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-copilot\"]},\"create_issue\":{\"expires\":1,\"max\":1}}" GH_AW_ISSUE_EXPIRES: "1" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_HIDE_OLDER_COMMENTS: "true" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-copilot" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/smoke-copilot-safe-inputs.lock.yml b/.github/workflows/smoke-copilot-safe-inputs.lock.yml index 83d8dfc000..b248732bdb 100644 --- a/.github/workflows/smoke-copilot-safe-inputs.lock.yml +++ b/.github/workflows/smoke-copilot-safe-inputs.lock.yml @@ -1317,9 +1317,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-copilot-safe-inputs" GH_AW_WORKFLOW_NAME: "Smoke Copilot Safe Inputs" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1342,32 +1341,19 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-copilot\"]}}" GH_AW_HIDE_OLDER_COMMENTS: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-copilot" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index ffe258d2e9..c0db08df6f 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1428,12 +1428,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-copilot" GH_AW_WORKFLOW_NAME: "Smoke Copilot" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - add_labels_labels_added: ${{ steps.add_labels.outputs.labels_added }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1456,50 +1452,21 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-copilot\"]},\"create_issue\":{\"expires\":1,\"max\":1}}" GH_AW_ISSUE_EXPIRES: "1" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_HIDE_OLDER_COMMENTS: "true" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Add Labels - id: add_labels - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_LABELS_ALLOWED: "smoke-copilot" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_labels.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index ffbe8b016c..05fc560cf8 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -1721,11 +1721,8 @@ jobs: GH_AW_WORKFLOW_ID: "smoke-detector" GH_AW_WORKFLOW_NAME: "Smoke Detector - Smoke Test Failure Investigator" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1748,39 +1745,23 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1,\"target\":\"*\"},\"create_issue\":{\"expires\":1,\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[smoke-detector] " GH_AW_ISSUE_LABELS: "smoke-test,investigation" GH_AW_ISSUE_EXPIRES: "1" - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_COMMENT_TARGET: "*" GH_AW_HIDE_OLDER_COMMENTS: "true" - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/speckit-dispatcher.lock.yml b/.github/workflows/speckit-dispatcher.lock.yml index 2bae58c53c..de21138ca2 100644 --- a/.github/workflows/speckit-dispatcher.lock.yml +++ b/.github/workflows/speckit-dispatcher.lock.yml @@ -1799,11 +1799,8 @@ jobs: GH_AW_WORKFLOW_ID: "speckit-dispatcher" GH_AW_WORKFLOW_NAME: "Spec-Kit Command Dispatcher" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1826,34 +1823,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":5},\"create_issue\":{\"max\":5}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - name: Link Sub Issue id: link_sub_issue diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index 6f856fbd46..fb9fd83cef 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -2063,9 +2063,8 @@ jobs: GH_AW_WORKFLOW_ID: "stale-repo-identifier" GH_AW_WORKFLOW_NAME: "Stale Repository Identifier" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -2088,12 +2087,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":10}}" GH_AW_ISSUE_TITLE_PREFIX: "[Stale Repository] " GH_AW_ISSUE_LABELS: "stale-repository,automated-analysis" with: @@ -2101,7 +2100,7 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 5122d43f67..3e473163d1 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -1665,8 +1665,8 @@ jobs: GH_AW_WORKFLOW_ID: "static-analysis-report" GH_AW_WORKFLOW_NAME: "Static Analysis Report" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1689,18 +1689,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"security\",\"close_older_discussions\":true,\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index 6095658f5d..08a5216aa3 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -1347,8 +1347,8 @@ jobs: GH_AW_WORKFLOW_ID: "sub-issue-closer" GH_AW_WORKFLOW_NAME: "Sub-Issue Closer" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1371,31 +1371,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_COMMENT_TARGET: "*" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":20,\"target\":\"*\"},\"update_issue\":{\"allow_status\":true,\"max\":20,\"target\":\"*\"}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Update Issue - id: update_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_issue')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/update_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 50d208a337..2fbc415cde 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -1415,9 +1415,8 @@ jobs: GH_AW_WORKFLOW_ID: "super-linter" GH_AW_WORKFLOW_NAME: "Super Linter Report" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1440,12 +1439,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[linter] " GH_AW_ISSUE_LABELS: "automation,code-quality" with: @@ -1453,7 +1452,7 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); super_linter: diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 6146372a96..c2a1054aaa 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1766,10 +1766,10 @@ jobs: GH_AW_WORKFLOW_ID: "technical-doc-writer" GH_AW_WORKFLOW_NAME: "Technical Doc Writer" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1817,6 +1817,19 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); - name: Create Pull Request id: create_pull_request if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) @@ -1837,21 +1850,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); update_cache_memory: needs: diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml index cb993e9104..f9bf77068b 100644 --- a/.github/workflows/terminal-stylist.lock.yml +++ b/.github/workflows/terminal-stylist.lock.yml @@ -1338,8 +1338,8 @@ jobs: GH_AW_WORKFLOW_ID: "terminal-stylist" GH_AW_WORKFLOW_NAME: "Terminal Stylist" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1362,17 +1362,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"General\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 7714250597..cec17fd827 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -1779,8 +1779,8 @@ jobs: GH_AW_WORKFLOW_ID: "typist" GH_AW_WORKFLOW_NAME: "Typist - Go Type Analysis" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1803,17 +1803,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"General\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 88fc6cc426..4e2f628a3b 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1834,10 +1834,10 @@ jobs: GH_AW_WORKFLOW_ID: "unbloat-docs" GH_AW_WORKFLOW_NAME: "Documentation Unbloat" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} create_pull_request_pull_request_number: ${{ steps.create_pull_request.outputs.pull_request_number }} create_pull_request_pull_request_url: ${{ steps.create_pull_request.outputs.pull_request_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1885,6 +1885,19 @@ jobs: SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); - name: Create Pull Request id: create_pull_request if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) @@ -1907,21 +1920,6 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/tmp/gh-aw/actions/create_pull_request.cjs'); await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }} - GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); update_cache_memory: needs: diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index 41a9101f41..6c160d0d36 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -1509,9 +1509,8 @@ jobs: GH_AW_WORKFLOW_ID: "video-analyzer" GH_AW_WORKFLOW_NAME: "Video Analysis Agent" outputs: - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1534,12 +1533,12 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"max\":1}}" GH_AW_ISSUE_TITLE_PREFIX: "[video-analysis] " GH_AW_ISSUE_LABELS: "automation,video-processing" with: @@ -1547,6 +1546,6 @@ jobs: script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 27acf24a16..048cd20fa9 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -1868,8 +1868,8 @@ jobs: GH_AW_WORKFLOW_ID: "weekly-issue-summary" GH_AW_WORKFLOW_NAME: "Weekly Issue Summary" outputs: - create_discussion_discussion_number: ${{ steps.create_discussion.outputs.discussion_number }} - create_discussion_discussion_url: ${{ steps.create_discussion.outputs.discussion_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1892,18 +1892,18 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Discussion - id: create_discussion - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_discussion')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"Audits\",\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_discussion.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); update_cache_memory: diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 7c39d93b2a..fbfe69e9da 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -1362,6 +1362,8 @@ jobs: GH_AW_WORKFLOW_NAME: "Workflow Generator" outputs: assign_to_agent_assigned: ${{ steps.assign_to_agent.outputs.assigned }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1384,30 +1386,30 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Assign To Agent - id: assign_to_agent - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"update_issue\":{\"allow_body\":true,\"allow_status\":true,\"max\":1,\"target\":\"${{ github.event.issue.number }}\"}}" with: - github-token: ${{ secrets.GH_AW_AGENT_TOKEN }} + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/assign_to_agent.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - - name: Update Issue - id: update_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_issue')) + - name: Assign To Agent + id: assign_to_agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent')) uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GH_AW_AGENT_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/update_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/assign_to_agent.cjs'); await main(); diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index b7a3dc6459..39b079d2fa 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -1815,11 +1815,8 @@ jobs: GH_AW_WORKFLOW_ID: "workflow-health-manager" GH_AW_WORKFLOW_NAME: "Workflow Health Manager - Meta-Orchestrator" outputs: - add_comment_comment_id: ${{ steps.add_comment.outputs.comment_id }} - add_comment_comment_url: ${{ steps.add_comment.outputs.comment_url }} - create_issue_issue_number: ${{ steps.create_issue.outputs.issue_number }} - create_issue_issue_url: ${{ steps.create_issue.outputs.issue_url }} - create_issue_temporary_id_map: ${{ steps.create_issue.outputs.temporary_id_map }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Checkout actions folder uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 @@ -1842,46 +1839,17 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Create Issue - id: create_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_issue')) + - name: Process Safe Outputs + id: process_safe_outputs uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":15},\"create_issue\":{\"max\":10},\"update_issue\":{\"max\":5}}" with: github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/create_issue.cjs'); - await main(); - - name: Add Comment - id: add_comment - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }} - GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }} - GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/add_comment.cjs'); - await main(); - - name: Update Issue - id: update_issue - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'update_issue')) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/tmp/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/tmp/gh-aw/actions/update_issue.cjs'); + const { main } = require('/tmp/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); diff --git a/actions/setup/js/add_comment.cjs b/actions/setup/js/add_comment.cjs index 40b8e25dec..c4d6e462a8 100644 --- a/actions/setup/js/add_comment.cjs +++ b/actions/setup/js/add_comment.cjs @@ -262,11 +262,11 @@ async function commentOnDiscussion(github, owner, repo, discussionNumber, messag }; } -async function main() { +async function main(config = {}) { // Check if we're in staged mode const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const isDiscussionExplicit = process.env.GITHUB_AW_COMMENT_DISCUSSION === "true"; - const hideOlderCommentsEnabled = process.env.GH_AW_HIDE_OLDER_COMMENTS === "true"; + const hideOlderCommentsEnabled = config.hide_older_comments === true; // Load the temporary ID map from create_issue job const temporaryIdMap = loadTemporaryIdMap(); @@ -293,8 +293,8 @@ async function main() { return item.item_number; } - // Get the target configuration from environment variable - const commentTarget = process.env.GH_AW_COMMENT_TARGET || "triggering"; + // Get the target configuration from config object + const commentTarget = config.target || "triggering"; core.info(`Comment target configuration: ${commentTarget}`); // Check if we're in an issue, pull request, or discussion context diff --git a/actions/setup/js/add_labels.cjs b/actions/setup/js/add_labels.cjs index b50ad13c49..61d30cbfd5 100644 --- a/actions/setup/js/add_labels.cjs +++ b/actions/setup/js/add_labels.cjs @@ -5,7 +5,7 @@ const { processSafeOutput } = require("./safe_output_processor.cjs"); const { validateLabels } = require("./safe_output_validator.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); -async function main() { +async function main(config = {}) { // Use shared processor for common steps const result = await processSafeOutput( { @@ -16,9 +16,10 @@ async function main() { supportsPR: true, supportsIssue: true, envVars: { - allowed: "GH_AW_LABELS_ALLOWED", - maxCount: "GH_AW_LABELS_MAX_COUNT", - target: "GH_AW_LABELS_TARGET", + // Config values now passed via config object, not env vars + allowed: null, + maxCount: null, + target: null, }, }, { @@ -36,7 +37,8 @@ async function main() { } return content; }, - } + }, + config // Pass handler config as third parameter ); if (!result.success) { diff --git a/actions/setup/js/close_discussion.cjs b/actions/setup/js/close_discussion.cjs index 1262ff4be0..e3661f3b68 100644 --- a/actions/setup/js/close_discussion.cjs +++ b/actions/setup/js/close_discussion.cjs @@ -145,7 +145,7 @@ async function closeDiscussion(github, discussionId, reason) { return result.closeDiscussion.discussion; } -async function main() { +async function main(config = {}) { // Check if we're in staged mode const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; @@ -163,11 +163,11 @@ async function main() { core.info(`Found ${closeDiscussionItems.length} close-discussion item(s)`); - // Get configuration from environment - const requiredLabels = process.env.GH_AW_CLOSE_DISCUSSION_REQUIRED_LABELS ? process.env.GH_AW_CLOSE_DISCUSSION_REQUIRED_LABELS.split(",").map(l => l.trim()) : []; - const requiredTitlePrefix = process.env.GH_AW_CLOSE_DISCUSSION_REQUIRED_TITLE_PREFIX || ""; - const requiredCategory = process.env.GH_AW_CLOSE_DISCUSSION_REQUIRED_CATEGORY || ""; - const target = process.env.GH_AW_CLOSE_DISCUSSION_TARGET || "triggering"; + // Get configuration from config object (not environment variables) + const requiredLabels = config.required_labels || []; + const requiredTitlePrefix = config.required_title_prefix || ""; + const requiredCategory = config.required_category || ""; + const target = config.target || "triggering"; core.info(`Configuration: requiredLabels=${requiredLabels.join(",")}, requiredTitlePrefix=${requiredTitlePrefix}, requiredCategory=${requiredCategory}, target=${target}`); diff --git a/actions/setup/js/close_entity_helpers.cjs b/actions/setup/js/close_entity_helpers.cjs index fc3d2d5691..50e3cb127b 100644 --- a/actions/setup/js/close_entity_helpers.cjs +++ b/actions/setup/js/close_entity_helpers.cjs @@ -219,9 +219,10 @@ function escapeMarkdownTitle(title) { * Process close entity items from agent output * @param {EntityConfig} config - Entity configuration * @param {EntityCallbacks} callbacks - Entity-specific API callbacks + * @param {Object} handlerConfig - Handler-specific configuration object * @returns {Promise|undefined>} */ -async function processCloseEntityItems(config, callbacks) { +async function processCloseEntityItems(config, callbacks, handlerConfig = {}) { // Check if we're in staged mode const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; @@ -239,8 +240,10 @@ async function processCloseEntityItems(config, callbacks) { core.info(`Found ${items.length} ${config.itemTypeDisplay} item(s)`); - // Get configuration from environment - const { requiredLabels, requiredTitlePrefix, target } = parseEntityConfig(config.envVarPrefix); + // Get configuration from handlerConfig object (not environment variables) + const requiredLabels = handlerConfig.required_labels || []; + const requiredTitlePrefix = handlerConfig.required_title_prefix || ""; + const target = handlerConfig.target || "triggering"; core.info(`Configuration: requiredLabels=${requiredLabels.join(",")}, requiredTitlePrefix=${requiredTitlePrefix}, target=${target}`); diff --git a/actions/setup/js/close_issue.cjs b/actions/setup/js/close_issue.cjs index 557007de80..67a249aae2 100644 --- a/actions/setup/js/close_issue.cjs +++ b/actions/setup/js/close_issue.cjs @@ -64,12 +64,12 @@ async function closeIssue(github, owner, repo, issueNumber) { return issue; } -async function main() { +async function main(config = {}) { return processCloseEntityItems(ISSUE_CONFIG, { getDetails: getIssueDetails, addComment: addIssueComment, closeEntity: closeIssue, - }); + }, config); } module.exports = { main }; diff --git a/actions/setup/js/create_discussion.cjs b/actions/setup/js/create_discussion.cjs index 65c35ff07e..1c0a275b05 100644 --- a/actions/setup/js/create_discussion.cjs +++ b/actions/setup/js/create_discussion.cjs @@ -6,7 +6,6 @@ const { getTrackerID } = require("./get_tracker_id.cjs"); const { closeOlderDiscussions } = require("./close_older_discussions.cjs"); const { replaceTemporaryIdReferences, loadTemporaryIdMap } = require("./temporary_id.cjs"); const { parseAllowedRepos, getDefaultTargetRepo, validateRepo, parseRepoSlug } = require("./repo_helpers.cjs"); -const { addExpirationComment } = require("./expiration_helpers.cjs"); const { removeDuplicateTitleFromDescription } = require("./remove_duplicate_title.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); @@ -87,7 +86,7 @@ function resolveCategoryId(categoryConfig, itemCategory, categories) { return undefined; } -async function main() { +async function main(config = {}) { // Initialize outputs to empty strings to ensure they're always set core.setOutput("discussion_number", ""); core.setOutput("discussion_url", ""); @@ -110,8 +109,8 @@ async function main() { } core.info(`Found ${createDiscussionItems.length} create-discussion item(s)`); - // Parse allowed repos and default target - const allowedRepos = parseAllowedRepos(); + // Parse allowed repos from config and default target + const allowedRepos = parseAllowedRepos(config.allowed_repos); const defaultTargetRepo = getDefaultTargetRepo(); core.info(`Default target repo: ${defaultTargetRepo}`); if (allowedRepos.size > 0) { @@ -145,17 +144,18 @@ async function main() { /** @type {Map}>} */ const repoInfoCache = new Map(); - // Get configuration for close-older-discussions - const closeOlderEnabled = process.env.GH_AW_CLOSE_OLDER_DISCUSSIONS === "true"; - const titlePrefix = process.env.GH_AW_DISCUSSION_TITLE_PREFIX || ""; - const configCategory = process.env.GH_AW_DISCUSSION_CATEGORY || ""; - const labelsEnvVar = process.env.GH_AW_DISCUSSION_LABELS || ""; - const labels = labelsEnvVar - ? labelsEnvVar + // Get configuration from config object + const closeOlderEnabled = config.close_older_discussions === true; + const titlePrefix = config.title_prefix || ""; + const configCategory = config.category || ""; + // Parse labels from config (can be array or comma-separated string) + const labelsConfig = config.labels || []; + const labels = Array.isArray(labelsConfig) + ? labelsConfig + : String(labelsConfig) .split(",") .map(l => l.trim()) - .filter(l => l.length > 0) - : []; + .filter(l => l.length > 0); const workflowName = process.env.GH_AW_WORKFLOW_NAME || "Workflow"; const runId = context.runId; const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com"; @@ -256,8 +256,17 @@ async function main() { bodyLines.push(trackerIDComment); } - // Add expiration comment if expires is set - addExpirationComment(bodyLines, "GH_AW_DISCUSSION_EXPIRES", "Discussion"); + // Add expiration comment if expires is set in config + if (config.expires) { + const expiresDays = parseInt(String(config.expires), 10); + if (!isNaN(expiresDays) && expiresDays > 0) { + const expirationDate = new Date(); + expirationDate.setDate(expirationDate.getDate() + expiresDays); + const expirationISO = expirationDate.toISOString(); + bodyLines.push(``); + core.info(`Discussion will expire on ${expirationISO} (${expiresDays} days)`); + } + } bodyLines.push(``, ``, `> AI generated by [${workflowName}](${runUrl})`, ""); const body = bodyLines.join("\n").trim(); diff --git a/actions/setup/js/create_issue.cjs b/actions/setup/js/create_issue.cjs index e4e0ca0aa4..b66c8f91e8 100644 --- a/actions/setup/js/create_issue.cjs +++ b/actions/setup/js/create_issue.cjs @@ -12,7 +12,7 @@ const { addExpirationComment } = require("./expiration_helpers.cjs"); const { removeDuplicateTitleFromDescription } = require("./remove_duplicate_title.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); -async function main() { +async function main(config = {}) { // Initialize outputs to empty strings to ensure they're always set core.setOutput("issue_number", ""); core.setOutput("issue_url", ""); @@ -33,8 +33,8 @@ async function main() { } core.info(`Found ${createIssueItems.length} create-issue item(s)`); - // Parse allowed repos and default target - const allowedRepos = parseAllowedRepos(); + // Parse allowed repos from config and default target + const allowedRepos = parseAllowedRepos(config.allowed_repos); const defaultTargetRepo = getDefaultTargetRepo(); core.info(`Default target repo: ${defaultTargetRepo}`); if (allowedRepos.size > 0) { @@ -80,11 +80,10 @@ async function main() { const triggeringPRNumber = context.payload?.pull_request?.number || (context.payload?.issue?.pull_request ? context.payload.issue.number : undefined); const triggeringDiscussionNumber = context.payload?.discussion?.number; - const labelsEnv = process.env.GH_AW_ISSUE_LABELS; - let envLabels = labelsEnv - ? labelsEnv - .split(",") - .map(label => label.trim()) + // Read labels from config object + let envLabels = config.labels + ? (Array.isArray(config.labels) ? config.labels : config.labels.split(",")) + .map(label => String(label).trim()) .filter(label => label) : []; const createdIssues = []; @@ -178,7 +177,8 @@ async function main() { if (!title) { title = createIssueItem.body || "Agent Output"; } - const titlePrefix = process.env.GH_AW_ISSUE_TITLE_PREFIX; + // Read title_prefix from config object + const titlePrefix = config.title_prefix; if (titlePrefix && !title.startsWith(titlePrefix)) { title = titlePrefix + title; } @@ -204,8 +204,17 @@ async function main() { bodyLines.push(trackerIDComment); } - // Add expiration comment if expires is set - addExpirationComment(bodyLines, "GH_AW_ISSUE_EXPIRES", "Issue"); + // Add expiration comment if expires is set in config + if (config.expires) { + const expiresDays = parseInt(String(config.expires), 10); + if (!isNaN(expiresDays) && expiresDays > 0) { + const expirationDate = new Date(); + expirationDate.setDate(expirationDate.getDate() + expiresDays); + const expirationISO = expirationDate.toISOString(); + bodyLines.push(``); + core.info(`Issue will expire on ${expirationISO} (${expiresDays} days)`); + } + } bodyLines.push(``, ``, generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber).trimEnd(), ""); const body = bodyLines.join("\n").trim(); diff --git a/actions/setup/js/repo_helpers.cjs b/actions/setup/js/repo_helpers.cjs index ce0c5d87be..c25bb91f62 100644 --- a/actions/setup/js/repo_helpers.cjs +++ b/actions/setup/js/repo_helpers.cjs @@ -7,14 +7,19 @@ */ /** - * Parse the allowed repos from environment variable + * Parse the allowed repos from config value (array or comma-separated string) + * @param {string[]|string|undefined} allowedReposValue - Allowed repos from config (array or comma-separated string) * @returns {Set} Set of allowed repository slugs */ -function parseAllowedRepos() { - const allowedReposEnv = process.env.GH_AW_ALLOWED_REPOS; +function parseAllowedRepos(allowedReposValue) { const set = new Set(); - if (allowedReposEnv) { - allowedReposEnv + if (Array.isArray(allowedReposValue)) { + allowedReposValue + .map(repo => repo.trim()) + .filter(repo => repo) + .forEach(repo => set.add(repo)); + } else if (typeof allowedReposValue === "string") { + allowedReposValue .split(",") .map(repo => repo.trim()) .filter(repo => repo) diff --git a/actions/setup/js/repo_helpers.test.cjs b/actions/setup/js/repo_helpers.test.cjs index 5f1a24b614..bae1103dc9 100644 --- a/actions/setup/js/repo_helpers.test.cjs +++ b/actions/setup/js/repo_helpers.test.cjs @@ -13,48 +13,58 @@ global.context = mockContext; describe("repo_helpers", () => { beforeEach(() => { vi.resetModules(); - delete process.env.GH_AW_ALLOWED_REPOS; delete process.env.GH_AW_TARGET_REPO_SLUG; global.context = mockContext; }); describe("parseAllowedRepos", () => { - it("should return empty set when env var is not set", async () => { + it("should return empty set when value is undefined", async () => { const { parseAllowedRepos } = await import("./repo_helpers.cjs"); - const result = parseAllowedRepos(); + const result = parseAllowedRepos(undefined); expect(result.size).toBe(0); }); - it("should parse single repo from env var", async () => { - process.env.GH_AW_ALLOWED_REPOS = "org/repo-a"; + it("should parse single repo from string", async () => { const { parseAllowedRepos } = await import("./repo_helpers.cjs"); - const result = parseAllowedRepos(); + const result = parseAllowedRepos("org/repo-a"); expect(result.size).toBe(1); expect(result.has("org/repo-a")).toBe(true); }); - it("should parse multiple repos from comma-separated list", async () => { - process.env.GH_AW_ALLOWED_REPOS = "org/repo-a, org/repo-b, org/repo-c"; + it("should parse multiple repos from comma-separated string", async () => { const { parseAllowedRepos } = await import("./repo_helpers.cjs"); - const result = parseAllowedRepos(); + const result = parseAllowedRepos("org/repo-a, org/repo-b, org/repo-c"); expect(result.size).toBe(3); expect(result.has("org/repo-a")).toBe(true); expect(result.has("org/repo-b")).toBe(true); expect(result.has("org/repo-c")).toBe(true); }); - it("should trim whitespace from repo names", async () => { - process.env.GH_AW_ALLOWED_REPOS = " org/repo-a , org/repo-b "; + it("should parse repos from array", async () => { const { parseAllowedRepos } = await import("./repo_helpers.cjs"); - const result = parseAllowedRepos(); + const result = parseAllowedRepos(["org/repo-a", "org/repo-b"]); + expect(result.size).toBe(2); + expect(result.has("org/repo-a")).toBe(true); + expect(result.has("org/repo-b")).toBe(true); + }); + + it("should trim whitespace from repo names in string", async () => { + const { parseAllowedRepos } = await import("./repo_helpers.cjs"); + const result = parseAllowedRepos(" org/repo-a , org/repo-b "); + expect(result.has("org/repo-a")).toBe(true); + expect(result.has("org/repo-b")).toBe(true); + }); + + it("should trim whitespace from repo names in array", async () => { + const { parseAllowedRepos } = await import("./repo_helpers.cjs"); + const result = parseAllowedRepos([" org/repo-a ", " org/repo-b "]); expect(result.has("org/repo-a")).toBe(true); expect(result.has("org/repo-b")).toBe(true); }); it("should filter out empty strings", async () => { - process.env.GH_AW_ALLOWED_REPOS = "org/repo-a,,org/repo-b, ,"; const { parseAllowedRepos } = await import("./repo_helpers.cjs"); - const result = parseAllowedRepos(); + const result = parseAllowedRepos("org/repo-a,,org/repo-b, ,"); expect(result.size).toBe(2); }); }); diff --git a/actions/setup/js/safe_output_handler_manager.cjs b/actions/setup/js/safe_output_handler_manager.cjs new file mode 100644 index 0000000000..2fbfd7f2a1 --- /dev/null +++ b/actions/setup/js/safe_output_handler_manager.cjs @@ -0,0 +1,230 @@ +// @ts-check +/// + +/** + * Safe Output Handler Manager + * + * This module manages the dispatch of safe output messages to dedicated handlers. + * It reads configuration, loads the appropriate handlers for enabled safe output types, + * and processes messages from the agent output file while maintaining a shared temporary ID map. + */ + +const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); + +/** + * Handler map configuration + * Maps safe output types to their handler module file paths + */ +const HANDLER_MAP = { + create_issue: "./create_issue.cjs", + add_comment: "./add_comment.cjs", + create_discussion: "./create_discussion.cjs", + close_issue: "./close_issue.cjs", + close_discussion: "./close_discussion.cjs", + add_labels: "./add_labels.cjs", + update_issue: "./update_issue.cjs", + update_discussion: "./update_discussion.cjs", +}; + +/** + * Load configuration for safe outputs + * Reads configuration from GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG environment variable + * @returns {Object} Safe outputs configuration + */ +function loadConfig() { + if (!process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG) { + throw new Error("GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG environment variable is required but not set"); + } + + try { + const config = JSON.parse(process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG); + core.info(`Loaded config from GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: ${JSON.stringify(config)}`); + // Normalize config keys: convert hyphens to underscores + return Object.fromEntries(Object.entries(config).map(([k, v]) => [k.replace(/-/g, "_"), v])); + } catch (error) { + throw new Error(`Failed to parse GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: ${getErrorMessage(error)}`); + } +} + +/** + * Load and initialize handlers for enabled safe output types + * Calls each handler's factory function (main) to get message processors + * @param {Object} config - Safe outputs configuration + * @returns {Promise>} Map of type to message handler function + */ +async function loadHandlers(config) { + const messageHandlers = new Map(); + + core.info("Loading and initializing safe output handlers based on configuration..."); + + for (const [type, handlerPath] of Object.entries(HANDLER_MAP)) { + // Check if this safe output type is enabled in the config + // The presence of the config key indicates the handler should be loaded + if (config[type]) { + try { + const handlerModule = require(handlerPath); + if (handlerModule && typeof handlerModule.main === "function") { + // Call the factory function with config to get the message handler + const handlerConfig = config[type] || {}; + const messageHandler = await handlerModule.main(handlerConfig); + + if (typeof messageHandler !== "function") { + core.warning(`Handler ${type} main() did not return a function`); + continue; + } + + messageHandlers.set(type, messageHandler); + core.info(`✓ Loaded and initialized handler for: ${type}`); + } else { + core.warning(`Handler module ${type} does not export a main function`); + } + } catch (error) { + core.warning(`Failed to load handler for ${type}: ${getErrorMessage(error)}`); + } + } else { + core.debug(`Handler not enabled: ${type}`); + } + } + + core.info(`Loaded ${messageHandlers.size} handler(s)`); + return messageHandlers; +} + +/** + * Process all messages from agent output in the order they appear + * Dispatches each message to the appropriate handler while maintaining shared state (temporary ID map) + * + * @param {Map} messageHandlers - Map of message handler functions + * @param {Array} messages - Array of safe output messages + * @returns {Promise<{success: boolean, results: Array, temporaryIdMap: Map}>} + */ +async function processMessages(messageHandlers, messages) { + const results = []; + + // Initialize shared temporary ID map + // This will be populated by handlers as they create entities with temporary IDs + /** @type {Map} */ + const temporaryIdMap = new Map(); + + core.info(`Processing ${messages.length} message(s) in order of appearance...`); + + // Process messages in order of appearance + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; + const messageType = message.type; + + if (!messageType) { + core.warning(`Skipping message ${i + 1} without type`); + continue; + } + + const messageHandler = messageHandlers.get(messageType); + + if (!messageHandler) { + core.debug(`No handler for type: ${messageType} (message ${i + 1})`); + continue; + } + + try { + core.info(`Processing message ${i + 1}/${messages.length}: ${messageType}`); + + // Convert Map to plain object for handler + const resolvedTemporaryIds = Object.fromEntries(temporaryIdMap); + + // Call the message handler with the individual message and resolved temp IDs + const result = await messageHandler(message, resolvedTemporaryIds); + + // If handler returned a temp ID mapping, add it to our map + if (result && result.temporaryId && result.repo && result.number) { + temporaryIdMap.set(result.temporaryId, { + repo: result.repo, + number: result.number, + }); + core.info(`Registered temporary ID: ${result.temporaryId} -> ${result.repo}#${result.number}`); + } + + results.push({ + type: messageType, + messageIndex: i, + success: true, + result, + }); + + core.info(`✓ Message ${i + 1} (${messageType}) completed successfully`); + } catch (error) { + core.error(`✗ Message ${i + 1} (${messageType}) failed: ${getErrorMessage(error)}`); + results.push({ + type: messageType, + messageIndex: i, + success: false, + error: getErrorMessage(error), + }); + } + } + + // Convert temporaryIdMap to plain object for serialization + const temporaryIdMapObj = Object.fromEntries(temporaryIdMap); + + return { + success: true, + results, + temporaryIdMap: temporaryIdMapObj, + }; +} + +/** + * Main entry point for the handler manager + * This is called by the consolidated safe output step + * + * @returns {Promise} + */ +async function main() { + try { + core.info("Safe Output Handler Manager starting..."); + + // Load configuration + const config = loadConfig(); + core.debug(`Configuration: ${JSON.stringify(Object.keys(config))}`); + + // Load agent output + const agentOutput = loadAgentOutput(); + if (!agentOutput.success) { + core.info("No agent output available - nothing to process"); + return; + } + + core.info(`Found ${agentOutput.items.length} message(s) in agent output`); + + // Load and initialize handlers based on configuration (factory pattern) + const messageHandlers = await loadHandlers(config); + + if (messageHandlers.size === 0) { + core.info("No handlers loaded - nothing to process"); + return; + } + + // Process all messages in order of appearance + const processingResult = await processMessages(messageHandlers, agentOutput.items); + + // Log summary + const successCount = processingResult.results.filter((r) => r.success).length; + const failureCount = processingResult.results.filter((r) => !r.success).length; + + core.info(`\n=== Processing Summary ===`); + core.info(`Total messages: ${processingResult.results.length}`); + core.info(`Successful: ${successCount}`); + core.info(`Failed: ${failureCount}`); + core.info(`Temporary IDs registered: ${Object.keys(processingResult.temporaryIdMap).length}`); + + if (failureCount > 0) { + core.warning(`${failureCount} message(s) failed to process`); + } + + core.info("Safe Output Handler Manager completed"); + } catch (error) { + core.setFailed(`Handler manager failed: ${getErrorMessage(error)}`); + } +} + +module.exports = { main }; diff --git a/actions/setup/js/safe_output_handler_manager.test.cjs b/actions/setup/js/safe_output_handler_manager.test.cjs new file mode 100644 index 0000000000..1629d08d18 --- /dev/null +++ b/actions/setup/js/safe_output_handler_manager.test.cjs @@ -0,0 +1,170 @@ +// @ts-check + +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { loadConfig, loadHandlers, processMessages } from "./safe_output_handler_manager.cjs"; + +describe("Safe Output Handler Manager", () => { + beforeEach(() => { + // Mock global core + global.core = { + info: vi.fn(), + debug: vi.fn(), + warning: vi.fn(), + error: vi.fn(), + setOutput: vi.fn(), + setFailed: vi.fn(), + }; + }); + + afterEach(() => { + // Clean up environment variables + delete process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG; + }); + + describe("loadConfig", () => { + it("should load config from environment variable and normalize keys", () => { + const config = { + "create-issue": { max: 5 }, + "add-comment": { max: 1 }, + }; + + process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG = JSON.stringify(config); + + const result = loadConfig(); + + expect(result).toHaveProperty("create_issue"); + expect(result).toHaveProperty("add_comment"); + expect(result.create_issue).toEqual({ max: 5 }); + expect(result.add_comment).toEqual({ max: 1 }); + }); + + it("should throw error if environment variable is not set", () => { + expect(() => loadConfig()).toThrow("GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG environment variable is required but not set"); + }); + + it("should throw error if environment variable contains invalid JSON", () => { + process.env.GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG = "not json"; + expect(() => loadConfig()).toThrow("Failed to parse GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG"); + }); + }); + + describe("loadHandlers", () => { + it("should load handlers for enabled safe output types", () => { + const config = { + create_issue: { max: 1 }, + add_comment: { max: 1 }, + }; + + const handlers = loadHandlers(config); + + expect(handlers.size).toBeGreaterThan(0); + expect(handlers.has("create_issue")).toBe(true); + expect(handlers.has("add_comment")).toBe(true); + }); + + it("should not load handlers when config entry is missing", () => { + const config = { + create_issue: { max: 1 }, + // add_comment is not in config + }; + + const handlers = loadHandlers(config); + + expect(handlers.has("create_issue")).toBe(true); + expect(handlers.has("add_comment")).toBe(false); + }); + + it("should handle missing handlers gracefully", () => { + const config = { + nonexistent_handler: { max: 1 }, + }; + + const handlers = loadHandlers(config); + + expect(handlers.size).toBe(0); + }); + }); + + describe("processMessages", () => { + it("should process messages in order of appearance", async () => { + const messages = [ + { type: "add_comment", body: "Comment" }, + { type: "create_issue", title: "Issue" }, + ]; + + const mockHandler = { + main: vi.fn().mockResolvedValue({ success: true }), + }; + + const handlers = new Map([ + ["create_issue", mockHandler], + ["add_comment", mockHandler], + ]); + + const config = { + create_issue: { max: 5 }, + add_comment: { max: 1 }, + }; + + const result = await processMessages(handlers, config, messages); + + expect(result.success).toBe(true); + expect(result.results).toHaveLength(2); + + // Verify handlers were called with their specific config + expect(mockHandler.main).toHaveBeenCalledWith({ max: 1 }); // add_comment config + expect(mockHandler.main).toHaveBeenCalledWith({ max: 5 }); // create_issue config + + // Verify messages were processed in order of appearance (add_comment first, then create_issue) + expect(result.results[0].type).toBe("add_comment"); + expect(result.results[0].messageIndex).toBe(0); + expect(result.results[1].type).toBe("create_issue"); + expect(result.results[1].messageIndex).toBe(1); + }); + + it("should skip messages without type", async () => { + const messages = [{ type: "create_issue", title: "Issue" }, { title: "No type" }, { type: "add_comment", body: "Comment" }]; + + const mockHandler = { + main: vi.fn().mockResolvedValue({ success: true }), + }; + + const handlers = new Map([ + ["create_issue", mockHandler], + ["add_comment", mockHandler], + ]); + + const config = { + create_issue: { max: 5 }, + add_comment: { max: 1 }, + }; + + const result = await processMessages(handlers, config, messages); + + expect(result.success).toBe(true); + expect(result.results).toHaveLength(2); + expect(core.warning).toHaveBeenCalledWith("Skipping message 2 without type"); + }); + + it("should handle handler errors gracefully", async () => { + const messages = [{ type: "create_issue", title: "Issue" }]; + + const errorHandler = { + main: vi.fn().mockRejectedValue(new Error("Handler failed")), + }; + + const handlers = new Map([["create_issue", errorHandler]]); + + const config = { + create_issue: { max: 5 }, + }; + + const result = await processMessages(handlers, config, messages); + + expect(result.success).toBe(true); + expect(result.results).toHaveLength(1); + expect(result.results[0].success).toBe(false); + expect(result.results[0].error).toBe("Handler failed"); + }); + }); +}); diff --git a/actions/setup/js/safe_output_processor.cjs b/actions/setup/js/safe_output_processor.cjs index 07c728e728..35c95b97f7 100644 --- a/actions/setup/js/safe_output_processor.cjs +++ b/actions/setup/js/safe_output_processor.cjs @@ -46,7 +46,7 @@ const { getSafeOutputConfig, validateMaxCount } = require("./safe_output_validat * 1. Load agent output * 2. Find matching item(s) * 3. Handle staged mode - * 4. Parse configuration + * 4. Parse configuration (from passed config or fallback to env vars) * 5. Resolve target (for single-item processors) * * @param {ProcessorConfig} config - Processor configuration @@ -54,9 +54,10 @@ const { getSafeOutputConfig, validateMaxCount } = require("./safe_output_validat * @param {string} stagedPreviewOptions.title - Title for staged preview * @param {string} stagedPreviewOptions.description - Description for staged preview * @param {(item: any, index: number) => string} stagedPreviewOptions.renderItem - Function to render item in preview + * @param {Object} [handlerConfig] - Handler-specific configuration passed from handler manager * @returns {Promise} Processing result */ -async function processSafeOutput(config, stagedPreviewOptions) { +async function processSafeOutput(config, stagedPreviewOptions, handlerConfig = null) { const { itemType, configKey, displayName, itemTypeName, supportsPR = false, supportsIssue = false, findMultiple = false, envVars } = config; // Step 1: Load agent output @@ -100,30 +101,58 @@ async function processSafeOutput(config, stagedPreviewOptions) { } // Step 4: Parse configuration - const safeOutputConfig = getSafeOutputConfig(configKey); + // If handlerConfig is provided (from handler manager), use it; otherwise fall back to file config + env vars + let allowed, maxCount, target; - // Parse allowed items (from env or config) - const allowedEnvValue = envVars.allowed ? process.env[envVars.allowed] : undefined; - const allowed = parseAllowedItems(allowedEnvValue) || safeOutputConfig.allowed; - if (allowed) { - core.info(`Allowed ${itemTypeName}s: ${JSON.stringify(allowed)}`); + if (handlerConfig) { + // Use config passed from handler manager + core.debug(`Using handler config: ${JSON.stringify(handlerConfig)}`); + + // Parse allowed items from handlerConfig + allowed = handlerConfig.allowed || handlerConfig.allowed_labels || handlerConfig.allowed_repos; + if (Array.isArray(allowed)) { + core.info(`Allowed ${itemTypeName}s: ${JSON.stringify(allowed)}`); + } else if (typeof allowed === "string") { + allowed = parseAllowedItems(allowed); + core.info(`Allowed ${itemTypeName}s: ${JSON.stringify(allowed)}`); + } else { + core.info(`No ${itemTypeName} restrictions - any ${itemTypeName}s are allowed`); + } + + // Get max count from handlerConfig + maxCount = handlerConfig.max || 3; + core.info(`Max count: ${maxCount}`); + + // Get target from handlerConfig + target = handlerConfig.target || "triggering"; + core.info(`${displayName} target configuration: ${target}`); } else { - core.info(`No ${itemTypeName} restrictions - any ${itemTypeName}s are allowed`); - } + // Fall back to reading from config file + env vars (backward compatibility) + const safeOutputConfig = getSafeOutputConfig(configKey); - // Parse max count (env takes priority, then config) - const maxCountEnvValue = envVars.maxCount ? process.env[envVars.maxCount] : undefined; - const maxCountResult = validateMaxCount(maxCountEnvValue, safeOutputConfig.max); - if (!maxCountResult.valid) { - core.setFailed(maxCountResult.error); - return { success: false, reason: "Invalid max count configuration" }; - } - const maxCount = maxCountResult.value; - core.info(`Max count: ${maxCount}`); + // Parse allowed items (from env or config) + const allowedEnvValue = envVars.allowed ? process.env[envVars.allowed] : undefined; + allowed = parseAllowedItems(allowedEnvValue) || safeOutputConfig.allowed; + if (allowed) { + core.info(`Allowed ${itemTypeName}s: ${JSON.stringify(allowed)}`); + } else { + core.info(`No ${itemTypeName} restrictions - any ${itemTypeName}s are allowed`); + } - // Get target configuration - const target = envVars.target ? process.env[envVars.target] || "triggering" : "triggering"; - core.info(`${displayName} target configuration: ${target}`); + // Parse max count (env takes priority, then config) + const maxCountEnvValue = envVars.maxCount ? process.env[envVars.maxCount] : undefined; + const maxCountResult = validateMaxCount(maxCountEnvValue, safeOutputConfig.max); + if (!maxCountResult.valid) { + core.setFailed(maxCountResult.error); + return { success: false, reason: "Invalid max count configuration" }; + } + maxCount = maxCountResult.value; + core.info(`Max count: ${maxCount}`); + + // Get target configuration + target = envVars.target ? process.env[envVars.target] || "triggering" : "triggering"; + core.info(`${displayName} target configuration: ${target}`); + } // For multiple items, return early without target resolution if (findMultiple) { diff --git a/actions/setup/js/update_discussion.cjs b/actions/setup/js/update_discussion.cjs index 3b9161a593..39b351926c 100644 --- a/actions/setup/js/update_discussion.cjs +++ b/actions/setup/js/update_discussion.cjs @@ -11,14 +11,15 @@ const { generateFooterWithMessages } = require("./messages_footer.cjs"); * @param {any} context - GitHub Actions context * @param {number} discussionNumber - Discussion number to update * @param {any} updateData - Data to update + * @param {any} handlerConfig - Handler configuration object * @returns {Promise} Updated discussion */ -async function executeDiscussionUpdate(github, context, discussionNumber, updateData) { +async function executeDiscussionUpdate(github, context, discussionNumber, updateData, handlerConfig = {}) { // Remove internal fields used for operation handling const { _operation, _rawBody, labels, ...fieldsToUpdate } = updateData; - // Check if labels should be updated based on environment variable - const shouldUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true" && labels !== undefined; + // Check if labels should be updated based on handler config + const shouldUpdateLabels = handlerConfig.allow_labels === true && labels !== undefined; // First, fetch the discussion node ID using its number const getDiscussionQuery = shouldUpdateLabels diff --git a/actions/setup/js/update_issue.cjs b/actions/setup/js/update_issue.cjs index 3fd5564e18..2de14ff373 100644 --- a/actions/setup/js/update_issue.cjs +++ b/actions/setup/js/update_issue.cjs @@ -10,9 +10,10 @@ const { isIssueContext, getIssueNumber } = require("./update_context_helpers.cjs * @param {any} context - GitHub Actions context * @param {number} issueNumber - Issue number to update * @param {any} updateData - Data to update + * @param {any} handlerConfig - Handler configuration object (unused for issues, kept for consistency) * @returns {Promise} Updated issue */ -async function executeIssueUpdate(github, context, issueNumber, updateData) { +async function executeIssueUpdate(github, context, issueNumber, updateData, handlerConfig = {}) { // Remove internal fields used for operation handling const { _operation, _rawBody, ...apiData } = updateData; diff --git a/actions/setup/js/update_runner.cjs b/actions/setup/js/update_runner.cjs index 109f20d53f..cc1df2563b 100644 --- a/actions/setup/js/update_runner.cjs +++ b/actions/setup/js/update_runner.cjs @@ -158,7 +158,22 @@ function buildUpdateData(params) { * @returns {Promise} Array of updated items or undefined */ async function runUpdateWorkflow(config) { - const { itemType, displayName, displayNamePlural, numberField, outputNumberKey, outputUrlKey, isValidContext, getContextNumber, supportsStatus, supportsOperation, renderStagedItem, executeUpdate, getSummaryLine } = config; + const { + itemType, + displayName, + displayNamePlural, + numberField, + outputNumberKey, + outputUrlKey, + isValidContext, + getContextNumber, + supportsStatus, + supportsOperation, + renderStagedItem, + executeUpdate, + getSummaryLine, + handlerConfig = {}, + } = config; // Check if we're in staged mode const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; @@ -188,12 +203,12 @@ async function runUpdateWorkflow(config) { return; } - // Get the configuration from environment variables - const updateTarget = process.env.GH_AW_UPDATE_TARGET || "triggering"; - const canUpdateStatus = process.env.GH_AW_UPDATE_STATUS === "true"; - const canUpdateTitle = process.env.GH_AW_UPDATE_TITLE === "true"; - const canUpdateBody = process.env.GH_AW_UPDATE_BODY === "true"; - const canUpdateLabels = process.env.GH_AW_UPDATE_LABELS === "true"; + // Get the configuration from handler config object + const updateTarget = handlerConfig.target || "triggering"; + const canUpdateStatus = handlerConfig.allow_status === true; + const canUpdateTitle = handlerConfig.allow_title === true; + const canUpdateBody = handlerConfig.allow_body === true; + const canUpdateLabels = handlerConfig.allow_labels === true; core.info(`Update target configuration: ${updateTarget}`); if (supportsStatus) { @@ -267,7 +282,7 @@ async function runUpdateWorkflow(config) { try { // Execute the update using the provided function - const updatedItem = await executeUpdate(github, context, targetNumber, updateData); + const updatedItem = await executeUpdate(github, context, targetNumber, updateData, handlerConfig); core.info(`Updated ${displayName} #${updatedItem.number}: ${updatedItem.html_url}`); updatedItems.push(updatedItem); @@ -399,7 +414,7 @@ function createUpdateHandler(config) { }); // Return the main handler function - return async function main() { + return async function main(handlerConfig = {}) { return await runUpdateWorkflow({ itemType: config.itemType, displayName: config.displayName, @@ -414,6 +429,7 @@ function createUpdateHandler(config) { renderStagedItem, executeUpdate: config.executeUpdate, getSummaryLine, + handlerConfig, // Pass handler config to the runner }); }; } diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 36f5e4255c..302bd1baf5 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -1,30 +1,45 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "required": ["on"], + "required": [ + "on" + ], "properties": { "name": { "type": "string", "minLength": 1, "description": "Workflow name that appears in the GitHub Actions interface. If not specified, defaults to the filename without extension.", - "examples": ["Copilot Agent PR Analysis", "Dev Hawk", "Smoke Claude"] + "examples": [ + "Copilot Agent PR Analysis", + "Dev Hawk", + "Smoke Claude" + ] }, "description": { "type": "string", "description": "Optional workflow description that is rendered as a comment in the generated GitHub Actions YAML file (.lock.yml)", - "examples": ["Quickstart for using the GitHub Actions library"] + "examples": [ + "Quickstart for using the GitHub Actions library" + ] }, "source": { "type": "string", "description": "Optional source reference indicating where this workflow was added from. Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/ci-doctor.md@v1.0.0). Rendered as a comment in the generated lock file.", - "examples": ["githubnext/agentics/workflows/ci-doctor.md", "githubnext/agentics/workflows/daily-perf-improver.md@1f181b37d3fe5862ab590648f25a292e345b5de6"] + "examples": [ + "githubnext/agentics/workflows/ci-doctor.md", + "githubnext/agentics/workflows/daily-perf-improver.md@1f181b37d3fe5862ab590648f25a292e345b5de6" + ] }, "tracker-id": { "type": "string", "minLength": 8, "pattern": "^[a-zA-Z0-9_-]+$", "description": "Optional tracker identifier to tag all created assets (issues, discussions, comments, pull requests). Must be at least 8 characters and contain only alphanumeric characters, hyphens, and underscores. This identifier will be inserted in the body/description of all created assets to enable searching and retrieving assets associated with this workflow.", - "examples": ["workflow-2024-q1", "team-alpha-bot", "security_audit_v2"] + "examples": [ + "workflow-2024-q1", + "team-alpha-bot", + "security_audit_v2" + ] }, "labels": { "type": "array", @@ -34,9 +49,18 @@ "minLength": 1 }, "examples": [ - ["automation", "security"], - ["docs", "maintenance"], - ["ci", "testing"] + [ + "automation", + "security" + ], + [ + "docs", + "maintenance" + ], + [ + "ci", + "testing" + ] ] }, "metadata": { @@ -70,7 +94,9 @@ { "type": "object", "description": "Import specification with path and optional inputs", - "required": ["path"], + "required": [ + "path" + ], "additionalProperties": false, "properties": { "path": { @@ -99,10 +125,21 @@ ] }, "examples": [ - ["shared/jqschema.md", "shared/reporting.md"], - ["shared/mcp/gh-aw.md", "shared/jqschema.md", "shared/reporting.md"], - ["../instructions/documentation.instructions.md"], - [".github/agents/my-agent.md"], + [ + "shared/jqschema.md", + "shared/reporting.md" + ], + [ + "shared/mcp/gh-aw.md", + "shared/jqschema.md", + "shared/reporting.md" + ], + [ + "../instructions/documentation.instructions.md" + ], + [ + ".github/agents/my-agent.md" + ], [ { "path": "shared/discussions-data-fetch.md", @@ -120,7 +157,13 @@ "type": "string", "minLength": 1, "description": "Simple trigger event name (e.g., 'push', 'issues', 'pull_request', 'discussion', 'schedule', 'fork', 'create', 'delete', 'public', 'watch', 'workflow_call'), schedule shorthand (e.g., 'daily', 'weekly'), or slash command shorthand (e.g., '/my-bot' expands to slash_command + workflow_dispatch)", - "examples": ["push", "issues", "workflow_dispatch", "daily", "/my-bot"] + "examples": [ + "push", + "issues", + "workflow_dispatch", + "daily", + "/my-bot" + ] }, { "type": "object", @@ -155,7 +198,16 @@ { "type": "string", "description": "Single event name or '*' for all events. Use GitHub Actions event names: 'issues', 'issue_comment', 'pull_request_comment', 'pull_request', 'pull_request_review_comment', 'discussion', 'discussion_comment'.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] }, { "type": "array", @@ -164,7 +216,16 @@ "items": { "type": "string", "description": "GitHub Actions event name.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] } } ] @@ -203,7 +264,16 @@ { "type": "string", "description": "Single event name or '*' for all events. Use GitHub Actions event names: 'issues', 'issue_comment', 'pull_request_comment', 'pull_request', 'pull_request_review_comment', 'discussion', 'discussion_comment'.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] }, { "type": "array", @@ -212,7 +282,16 @@ "items": { "type": "string", "description": "GitHub Actions event name.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] } } ] @@ -276,25 +355,37 @@ }, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -304,25 +395,37 @@ { "oneOf": [ { - "required": ["paths"], + "required": [ + "paths" + ], "not": { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } }, { - "required": ["paths-ignore"], + "required": [ + "paths-ignore" + ], "not": { - "required": ["paths"] + "required": [ + "paths" + ] } }, { "not": { "anyOf": [ { - "required": ["paths"] + "required": [ + "paths" + ] }, { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } ] } @@ -439,25 +542,37 @@ "additionalProperties": false, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -467,25 +582,37 @@ { "oneOf": [ { - "required": ["paths"], + "required": [ + "paths" + ], "not": { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } }, { - "required": ["paths-ignore"], + "required": [ + "paths-ignore" + ], "not": { - "required": ["paths"] + "required": [ + "paths" + ] } }, { "not": { "anyOf": [ { - "required": ["paths"] + "required": [ + "paths" + ] }, { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } ] } @@ -504,7 +631,26 @@ "description": "Types of issue events", "items": { "type": "string", - "enum": ["opened", "edited", "deleted", "transferred", "pinned", "unpinned", "closed", "reopened", "assigned", "unassigned", "labeled", "unlabeled", "locked", "unlocked", "milestoned", "demilestoned", "typed", "untyped"] + "enum": [ + "opened", + "edited", + "deleted", + "transferred", + "pinned", + "unpinned", + "closed", + "reopened", + "assigned", + "unassigned", + "labeled", + "unlabeled", + "locked", + "unlocked", + "milestoned", + "demilestoned", + "typed", + "untyped" + ] } }, "names": { @@ -540,7 +686,11 @@ "description": "Types of issue comment events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } }, "lock-for-agent": { @@ -559,7 +709,21 @@ "description": "Types of discussion events", "items": { "type": "string", - "enum": ["created", "edited", "deleted", "transferred", "pinned", "unpinned", "labeled", "unlabeled", "locked", "unlocked", "category_changed", "answered", "unanswered"] + "enum": [ + "created", + "edited", + "deleted", + "transferred", + "pinned", + "unpinned", + "labeled", + "unlabeled", + "locked", + "unlocked", + "category_changed", + "answered", + "unanswered" + ] } } } @@ -574,7 +738,11 @@ "description": "Types of discussion comment events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -599,7 +767,9 @@ "description": "Cron expression using standard format (e.g., '0 9 * * 1') or human-friendly format (e.g., 'daily at 02:00', 'daily at 3pm', 'daily at 6am', 'weekly on monday', 'weekly on friday at 5pm', 'every 10 minutes', 'every 2h', 'daily at 02:00 utc+9', 'daily at 3pm utc+9'). Human-friendly formats support: daily/weekly/monthly schedules with optional time, interval schedules (minimum 5 minutes), short duration units (m/h/d/w/mo), 12-hour time format (Npm/Nam where N is 1-12), and UTC timezone offsets (utc+N or utc+HH:MM)." } }, - "required": ["cron"], + "required": [ + "cron" + ], "additionalProperties": false } } @@ -638,7 +808,11 @@ }, "type": { "type": "string", - "enum": ["string", "choice", "boolean"], + "enum": [ + "string", + "choice", + "boolean" + ], "description": "Input type" }, "options": { @@ -672,7 +846,11 @@ "description": "Types of workflow run events", "items": { "type": "string", - "enum": ["completed", "requested", "in_progress"] + "enum": [ + "completed", + "requested", + "in_progress" + ] } }, "branches": { @@ -694,25 +872,37 @@ }, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -729,7 +919,15 @@ "description": "Types of release events", "items": { "type": "string", - "enum": ["published", "unpublished", "created", "edited", "deleted", "prereleased", "released"] + "enum": [ + "published", + "unpublished", + "created", + "edited", + "deleted", + "prereleased", + "released" + ] } } } @@ -744,7 +942,11 @@ "description": "Types of pull request review comment events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -759,7 +961,11 @@ "description": "Types of branch protection rule events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -774,7 +980,12 @@ "description": "Types of check run events", "items": { "type": "string", - "enum": ["created", "rerequested", "completed", "requested_action"] + "enum": [ + "created", + "rerequested", + "completed", + "requested_action" + ] } } } @@ -789,7 +1000,9 @@ "description": "Types of check suite events", "items": { "type": "string", - "enum": ["completed"] + "enum": [ + "completed" + ] } } } @@ -882,7 +1095,11 @@ "description": "Types of label events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -897,7 +1114,9 @@ "description": "Types of merge group events", "items": { "type": "string", - "enum": ["checks_requested"] + "enum": [ + "checks_requested" + ] } } } @@ -912,7 +1131,13 @@ "description": "Types of milestone events", "items": { "type": "string", - "enum": ["created", "closed", "opened", "edited", "deleted"] + "enum": [ + "created", + "closed", + "opened", + "edited", + "deleted" + ] } } } @@ -1029,25 +1254,37 @@ "additionalProperties": false, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -1057,25 +1294,37 @@ { "oneOf": [ { - "required": ["paths"], + "required": [ + "paths" + ], "not": { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } }, { - "required": ["paths-ignore"], + "required": [ + "paths-ignore" + ], "not": { - "required": ["paths"] + "required": [ + "paths" + ] } }, { "not": { "anyOf": [ { - "required": ["paths"] + "required": [ + "paths" + ] }, { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } ] } @@ -1094,7 +1343,11 @@ "description": "Types of pull request review events", "items": { "type": "string", - "enum": ["submitted", "edited", "dismissed"] + "enum": [ + "submitted", + "edited", + "dismissed" + ] } } } @@ -1109,7 +1362,10 @@ "description": "Types of registry package events", "items": { "type": "string", - "enum": ["published", "updated"] + "enum": [ + "published", + "updated" + ] } } } @@ -1151,7 +1407,9 @@ "description": "Types of watch events", "items": { "type": "string", - "enum": ["started"] + "enum": [ + "started" + ] } } } @@ -1183,7 +1441,11 @@ }, "type": { "type": "string", - "enum": ["string", "number", "boolean"], + "enum": [ + "string", + "number", + "boolean" + ], "description": "Type of the input parameter" }, "default": { @@ -1225,7 +1487,9 @@ }, { "type": "object", - "required": ["query"], + "required": [ + "query" + ], "properties": { "query": { "type": "string", @@ -1251,17 +1515,37 @@ "oneOf": [ { "type": "string", - "enum": ["+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes", "none"] + "enum": [ + "+1", + "-1", + "laugh", + "confused", + "heart", + "hooray", + "rocket", + "eyes", + "none" + ] }, { "type": "integer", - "enum": [1, -1], + "enum": [ + 1, + -1 + ], "description": "YAML parses +1 and -1 without quotes as integers. These are converted to +1 and -1 strings respectively." } ], "default": "eyes", "description": "AI reaction to add/remove on triggering item (one of: +1, -1, laugh, confused, heart, hooray, rocket, eyes, none). Use 'none' to disable reactions. Defaults to 'eyes' if not specified.", - "examples": ["eyes", "rocket", "+1", 1, -1, "none"] + "examples": [ + "eyes", + "rocket", + "+1", + 1, + -1, + "none" + ] } }, "additionalProperties": false, @@ -1277,25 +1561,37 @@ { "command": { "name": "mergefest", - "events": ["pull_request_comment"] + "events": [ + "pull_request_comment" + ] } }, { "workflow_run": { - "workflows": ["Dev"], - "types": ["completed"], - "branches": ["copilot/**"] + "workflows": [ + "Dev" + ], + "types": [ + "completed" + ], + "branches": [ + "copilot/**" + ] } }, { "pull_request": { - "types": ["ready_for_review"] + "types": [ + "ready_for_review" + ] }, "workflow_dispatch": null }, { "push": { - "branches": ["main"] + "branches": [ + "main" + ] } } ] @@ -1322,7 +1618,12 @@ "oneOf": [ { "type": "string", - "enum": ["read-all", "write-all", "read", "write"], + "enum": [ + "read-all", + "write-all", + "read", + "write" + ], "description": "Simple permissions string: 'read-all' (all read permissions), 'write-all' (all write permissions), 'read' or 'write' (basic level)" }, { @@ -1332,76 +1633,137 @@ "properties": { "actions": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for GitHub Actions workflows and runs (read: view workflows, write: manage workflows, none: no access)" }, "attestations": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for artifact attestations (read: view attestations, write: create attestations, none: no access)" }, "checks": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository checks and status checks (read: view checks, write: create/update checks, none: no access)" }, "contents": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository contents (read: view files, write: modify files/branches, none: no access)" }, "deployments": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository deployments (read: view deployments, write: create/update deployments, none: no access)" }, "discussions": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository discussions (read: view discussions, write: create/update discussions, none: no access)" }, "id-token": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "issues": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository issues (read: view issues, write: create/update/close issues, none: no access)" }, "models": { "type": "string", - "enum": ["read", "none"], + "enum": [ + "read", + "none" + ], "description": "Permission for GitHub Copilot models (read: access AI models for agentic workflows, none: no access)" }, "metadata": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository metadata (read: view repository information, write: update repository metadata, none: no access)" }, "packages": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "pages": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "pull-requests": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "security-events": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "statuses": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "all": { "type": "string", - "enum": ["read"], + "enum": [ + "read" + ], "description": "Permission shorthand that applies read access to all permission scopes. Can be combined with specific write permissions to override individual scopes. 'write' is not allowed for all." } } @@ -1411,7 +1773,10 @@ "run-name": { "type": "string", "description": "Custom name for workflow runs that appears in the GitHub Actions interface (supports GitHub expressions like ${{ github.event.issue.title }})", - "examples": ["Deploy to ${{ github.event.inputs.environment }}", "Build #${{ github.run_number }}"] + "examples": [ + "Deploy to ${{ github.event.inputs.environment }}", + "Build #${{ github.run_number }}" + ] }, "jobs": { "type": "object", @@ -1453,10 +1818,14 @@ "additionalProperties": false, "oneOf": [ { - "required": ["uses"] + "required": [ + "uses" + ] }, { - "required": ["run"] + "required": [ + "run" + ] } ], "properties": { @@ -1666,22 +2035,35 @@ ], "examples": [ "ubuntu-latest", - ["ubuntu-latest", "self-hosted"], + [ + "ubuntu-latest", + "self-hosted" + ], { "group": "larger-runners", - "labels": ["ubuntu-latest-8-cores"] + "labels": [ + "ubuntu-latest-8-cores" + ] } ] }, "timeout-minutes": { "type": "integer", "description": "Workflow timeout in minutes (GitHub Actions standard field). Defaults to 20 minutes for agentic workflows. Has sensible defaults and can typically be omitted.", - "examples": [5, 10, 30] + "examples": [ + 5, + 10, + 30 + ] }, "timeout_minutes": { "type": "integer", "description": "Deprecated: Use 'timeout-minutes' instead. Workflow timeout in minutes. Defaults to 20 minutes for agentic workflows.", - "examples": [5, 10, 30], + "examples": [ + 5, + 10, + 30 + ], "deprecated": true }, "concurrency": { @@ -1690,7 +2072,10 @@ { "type": "string", "description": "Simple concurrency group name to prevent multiple runs in the same group. Use expressions like '${{ github.workflow }}' for per-workflow isolation or '${{ github.ref }}' for per-branch isolation. Agentic workflows automatically generate enhanced concurrency policies using 'gh-aw-{engine-id}' as the default group to limit concurrent AI workloads across all workflows using the same engine.", - "examples": ["my-workflow-group", "workflow-${{ github.ref }}"] + "examples": [ + "my-workflow-group", + "workflow-${{ github.ref }}" + ] }, { "type": "object", @@ -1706,7 +2091,9 @@ "description": "Whether to cancel in-progress workflows in the same concurrency group when a new one starts. Default: false (queue new runs). Set to true for agentic workflows where only the latest run matters (e.g., PR analysis that becomes stale when new commits are pushed)." } }, - "required": ["group"], + "required": [ + "group" + ], "examples": [ { "group": "dev-workflow-${{ github.ref }}", @@ -1783,7 +2170,9 @@ "description": "A deployment URL" } }, - "required": ["name"], + "required": [ + "name" + ], "additionalProperties": false } ] @@ -1849,7 +2238,9 @@ "description": "Additional Docker container options" } }, - "required": ["image"], + "required": [ + "image" + ], "additionalProperties": false } ] @@ -1917,7 +2308,9 @@ "description": "Additional Docker container options" } }, - "required": ["image"], + "required": [ + "image" + ], "additionalProperties": false } ] @@ -1929,13 +2322,24 @@ "examples": [ "defaults", { - "allowed": ["defaults", "github"] + "allowed": [ + "defaults", + "github" + ] }, { - "allowed": ["defaults", "python", "node", "*.example.com"] + "allowed": [ + "defaults", + "python", + "node", + "*.example.com" + ] }, { - "allowed": ["api.openai.com", "*.github.com"], + "allowed": [ + "api.openai.com", + "*.github.com" + ], "firewall": { "version": "v1.0.0", "log-level": "debug" @@ -1945,7 +2349,9 @@ "oneOf": [ { "type": "string", - "enum": ["defaults"], + "enum": [ + "defaults" + ], "description": "Use default network permissions (basic infrastructure: certificates, JSON schema, Ubuntu, etc.)" }, { @@ -1976,7 +2382,9 @@ }, { "type": "string", - "enum": ["disable"], + "enum": [ + "disable" + ], "description": "Disable AWF firewall (triggers warning if allowed != *, error in strict mode if allowed is not * or engine does not support firewall)" }, { @@ -1991,14 +2399,27 @@ } }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "AWF version to use (empty = latest release). Can be a string (e.g., 'v1.0.0', 'latest') or number (e.g., 20, 3.11). Numeric values are automatically converted to strings at runtime.", - "examples": ["v1.0.0", "latest", 20, 3.11] + "examples": [ + "v1.0.0", + "latest", + 20, + 3.11 + ] }, "log-level": { "type": "string", "description": "AWF log level (default: info). Valid values: debug, info, warn, error", - "enum": ["debug", "info", "warn", "error"] + "enum": [ + "debug", + "info", + "warn", + "error" + ] } }, "additionalProperties": false @@ -2015,7 +2436,12 @@ "oneOf": [ { "type": "string", - "enum": ["default", "sandbox-runtime", "awf", "srt"], + "enum": [ + "default", + "sandbox-runtime", + "awf", + "srt" + ], "description": "Legacy string format for sandbox type: 'default' for no sandbox, 'sandbox-runtime' or 'srt' for Anthropic Sandbox Runtime, 'awf' for Agent Workflow Firewall" }, { @@ -2024,7 +2450,12 @@ "properties": { "type": { "type": "string", - "enum": ["default", "sandbox-runtime", "awf", "srt"], + "enum": [ + "default", + "sandbox-runtime", + "awf", + "srt" + ], "description": "Legacy sandbox type field (use agent instead)" }, "agent": { @@ -2032,12 +2463,17 @@ "oneOf": [ { "type": "boolean", - "enum": [false], + "enum": [ + false + ], "description": "Set to false to disable the agent firewall" }, { "type": "string", - "enum": ["awf", "srt"], + "enum": [ + "awf", + "srt" + ], "description": "Sandbox type: 'awf' for Agent Workflow Firewall, 'srt' for Sandbox Runtime" }, { @@ -2046,12 +2482,18 @@ "properties": { "id": { "type": "string", - "enum": ["awf", "srt"], + "enum": [ + "awf", + "srt" + ], "description": "Agent identifier (replaces 'type' field in new format): 'awf' for Agent Workflow Firewall, 'srt' for Sandbox Runtime" }, "type": { "type": "string", - "enum": ["awf", "srt"], + "enum": [ + "awf", + "srt" + ], "description": "Legacy: Sandbox type to use (use 'id' instead)" }, "command": { @@ -2080,7 +2522,12 @@ "pattern": "^[^:]+:[^:]+:(ro|rw)$", "description": "Mount specification in format 'source:destination:mode'" }, - "examples": [["/host/data:/data:ro", "/usr/local/bin/custom-tool:/usr/local/bin/custom-tool:ro"]] + "examples": [ + [ + "/host/data:/data:ro", + "/usr/local/bin/custom-tool:/usr/local/bin/custom-tool:ro" + ] + ] }, "config": { "type": "object", @@ -2194,9 +2641,15 @@ "description": "Container image for the MCP gateway executable" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version/tag for the container image (e.g., 'latest', 'v1.0.0')", - "examples": ["latest", "v1.0.0"] + "examples": [ + "latest", + "v1.0.0" + ] }, "args": { "type": "array", @@ -2238,29 +2691,41 @@ "additionalProperties": false, "anyOf": [ { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ], "not": { "allOf": [ { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ] }, "allOf": [ { "if": { - "required": ["entrypointArgs"] + "required": [ + "entrypointArgs" + ] }, "then": { - "required": ["container"] + "required": [ + "container" + ] } } ] @@ -2283,7 +2748,10 @@ "type": "srt", "config": { "filesystem": { - "allowWrite": [".", "/tmp"] + "allowWrite": [ + ".", + "/tmp" + ] } } } @@ -2307,7 +2775,10 @@ "if": { "type": "string", "description": "Conditional execution expression", - "examples": ["${{ github.event.workflow_run.event == 'workflow_dispatch' }}", "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}"] + "examples": [ + "${{ github.event.workflow_run.event == 'workflow_dispatch' }}", + "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}" + ] }, "steps": { "description": "Custom workflow steps", @@ -2439,13 +2910,24 @@ }, "mode": { "type": "string", - "enum": ["local", "remote"], + "enum": [ + "local", + "remote" + ], "description": "MCP server mode: 'local' (Docker-based, default) or 'remote' (hosted at api.githubcopilot.com)" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version specification for the GitHub MCP server (used with 'local' type). Can be a string (e.g., 'v1.0.0', 'latest') or number (e.g., 20, 3.11). Numeric values are automatically converted to strings at runtime.", - "examples": ["v1.0.0", "latest", 20, 3.11] + "examples": [ + "v1.0.0", + "latest", + 20, + 3.11 + ] }, "args": { "type": "array", @@ -2505,16 +2987,30 @@ "additionalProperties": false, "examples": [ { - "toolsets": ["pull_requests", "actions", "repos"] + "toolsets": [ + "pull_requests", + "actions", + "repos" + ] }, { - "allowed": ["search_pull_requests", "pull_request_read", "list_pull_requests", "get_file_contents", "list_commits", "get_commit"] + "allowed": [ + "search_pull_requests", + "pull_request_read", + "list_pull_requests", + "get_file_contents", + "list_commits", + "get_commit" + ] }, { "read-only": true }, { - "toolsets": ["pull_requests", "repos"] + "toolsets": [ + "pull_requests", + "repos" + ] } ] } @@ -2522,14 +3018,25 @@ "examples": [ null, { - "toolsets": ["pull_requests", "actions", "repos"] + "toolsets": [ + "pull_requests", + "actions", + "repos" + ] }, { - "allowed": ["search_pull_requests", "pull_request_read", "get_file_contents"] + "allowed": [ + "search_pull_requests", + "pull_request_read", + "get_file_contents" + ] }, { "read-only": true, - "toolsets": ["repos", "issues"] + "toolsets": [ + "repos", + "issues" + ] }, false ] @@ -2556,10 +3063,36 @@ ], "examples": [ true, - ["git fetch", "git checkout", "git status", "git diff", "git log", "make recompile", "make fmt", "make lint", "make test-unit", "cat", "echo", "ls"], - ["echo", "ls", "cat"], - ["gh pr list *", "gh search prs *", "jq *"], - ["date *", "echo *", "cat", "ls"] + [ + "git fetch", + "git checkout", + "git status", + "git diff", + "git log", + "make recompile", + "make fmt", + "make lint", + "make test-unit", + "cat", + "echo", + "ls" + ], + [ + "echo", + "ls", + "cat" + ], + [ + "gh pr list *", + "gh search prs *", + "jq *" + ], + [ + "date *", + "echo *", + "cat", + "ls" + ] ] }, "web-fetch": { @@ -2616,9 +3149,16 @@ "description": "Playwright tool configuration with custom version and domain restrictions", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional Playwright container version (e.g., 'v1.41.0', 1.41, 20). Numeric values are automatically converted to strings at runtime.", - "examples": ["v1.41.0", 1.41, 20] + "examples": [ + "v1.41.0", + 1.41, + 20 + ] }, "allowed_domains": { "description": "Domains allowed for Playwright browser network access. Defaults to localhost only for security.", @@ -2660,7 +3200,10 @@ "description": "Enable agentic-workflows tool with default settings (same as true)" } ], - "examples": [true, null] + "examples": [ + true, + null + ] }, "cache-memory": { "description": "Cache memory MCP configuration for persistent memory storage", @@ -2736,7 +3279,10 @@ "description": "If true, only restore the cache without saving it back. Uses actions/cache/restore instead of actions/cache. No artifact upload step will be generated." } }, - "required": ["id", "key"], + "required": [ + "id", + "key" + ], "additionalProperties": false }, "minItems": 1, @@ -2780,7 +3326,11 @@ "type": "integer", "minimum": 1, "description": "Timeout in seconds for tool/MCP server operations. Applies to all tools and MCP servers if supported by the engine. Default varies by engine (Claude: 60s, Codex: 120s).", - "examples": [60, 120, 300] + "examples": [ + 60, + 120, + 300 + ] }, "startup-timeout": { "type": "integer", @@ -2799,7 +3349,14 @@ "description": "Short syntax: array of language identifiers to enable (e.g., [\"go\", \"typescript\"])", "items": { "type": "string", - "enum": ["go", "typescript", "python", "java", "rust", "csharp"] + "enum": [ + "go", + "typescript", + "python", + "java", + "rust", + "csharp" + ] } }, { @@ -2807,9 +3364,16 @@ "description": "Serena configuration with custom version and language-specific settings", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional Serena MCP version. Numeric values are automatically converted to strings at runtime.", - "examples": ["latest", "0.1.0", 1.0] + "examples": [ + "latest", + "0.1.0", + 1.0 + ] }, "args": { "type": "array", @@ -2832,7 +3396,10 @@ "type": "object", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Go version (e.g., \"1.21\", 1.21)" }, "go-mod-file": { @@ -2858,7 +3425,10 @@ "type": "object", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Node.js version for TypeScript (e.g., \"22\", 22)" } }, @@ -2876,7 +3446,10 @@ "type": "object", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Python version (e.g., \"3.12\", 3.12)" } }, @@ -2894,7 +3467,10 @@ "type": "object", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Java version (e.g., \"21\", 21)" } }, @@ -2912,7 +3488,10 @@ "type": "object", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Rust version (e.g., \"stable\", \"1.75\")" } }, @@ -2930,7 +3509,10 @@ "type": "object", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": ".NET version for C# (e.g., \"8.0\", 8.0)" } }, @@ -3140,16 +3722,29 @@ }, "mode": { "type": "string", - "enum": ["stdio", "http", "remote", "local"], + "enum": [ + "stdio", + "http", + "remote", + "local" + ], "description": "MCP server mode" }, "type": { "type": "string", - "enum": ["stdio", "http", "remote", "local"], + "enum": [ + "stdio", + "http", + "remote", + "local" + ], "description": "MCP server type" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Version of the MCP server" }, "toolsets": { @@ -3247,17 +3842,25 @@ "description": "If true, only checks if cache entry exists and skips download" } }, - "required": ["key", "path"], + "required": [ + "key", + "path" + ], "additionalProperties": false, "examples": [ { "key": "node-modules-${{ hashFiles('package-lock.json') }}", "path": "node_modules", - "restore-keys": ["node-modules-"] + "restore-keys": [ + "node-modules-" + ] }, { "key": "build-cache-${{ github.sha }}", - "path": ["dist", ".cache"], + "path": [ + "dist", + ".cache" + ], "restore-keys": "build-cache-", "fail-on-cache-miss": false } @@ -3316,7 +3919,10 @@ "description": "If true, only checks if cache entry exists and skips download" } }, - "required": ["key", "path"], + "required": [ + "key", + "path" + ], "additionalProperties": false } } @@ -3390,10 +3996,6 @@ }, "description": "List of additional repositories in format 'owner/repo' that issues can be created in. When specified, the agent can use a 'repo' field in the output to specify which repository to create the issue in. The target repository (current or target-repo) is always implicitly allowed." }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." - }, "expires": { "oneOf": [ { @@ -3414,16 +4016,25 @@ "examples": [ { "title-prefix": "[ca] ", - "labels": ["automation", "dependencies"], + "labels": [ + "automation", + "dependencies" + ], "assignees": "copilot" }, { "title-prefix": "[duplicate-code] ", - "labels": ["code-quality", "automated-analysis"], + "labels": [ + "code-quality", + "automated-analysis" + ], "assignees": "copilot" }, { - "allowed-repos": ["org/other-repo", "org/another-repo"], + "allowed-repos": [ + "org/other-repo", + "org/another-repo" + ], "title-prefix": "[cross-repo] " } ] @@ -3512,9 +4123,16 @@ "description": "Optional prefix for the discussion title" }, "category": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional discussion category. Can be a category ID (string or numeric value), category name, or category slug/route. If not specified, uses the first available category. Matched first against category IDs, then against category names, then against category slugs. Numeric values are automatically converted to strings at runtime.", - "examples": ["General", "audits", 123456789] + "examples": [ + "General", + "audits", + 123456789 + ] }, "labels": { "type": "array", @@ -3547,10 +4165,6 @@ }, "description": "List of additional repositories in format 'owner/repo' that discussions can be created in. When specified, the agent can use a 'repo' field in the output to specify which repository to create the discussion in. The target repository (current or target-repo) is always implicitly allowed." }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." - }, "close-older-discussions": { "type": "boolean", "description": "When true, automatically close older discussions matching the same title prefix or labels as 'outdated' with a comment linking to the new discussion. Requires title-prefix or labels to be set. Maximum 10 discussions will be closed. Only runs if discussion creation succeeds.", @@ -3591,12 +4205,17 @@ "close-older-discussions": true }, { - "labels": ["weekly-report", "automation"], + "labels": [ + "weekly-report", + "automation" + ], "category": "reports", "close-older-discussions": true }, { - "allowed-repos": ["org/other-repo"], + "allowed-repos": [ + "org/other-repo" + ], "category": "General" } ] @@ -3641,10 +4260,6 @@ "target-repo": { "type": "string", "description": "Target repository in format 'owner/repo' for cross-repository operations. Takes precedence over trial target repo settings." - }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." } }, "additionalProperties": false, @@ -3653,7 +4268,10 @@ "required-category": "Ideas" }, { - "required-labels": ["resolved", "completed"], + "required-labels": [ + "resolved", + "completed" + ], "max": 1 } ] @@ -3702,10 +4320,6 @@ "target-repo": { "type": "string", "description": "Target repository in format 'owner/repo' for cross-repository discussion updates. Takes precedence over trial target repo settings." - }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." } }, "additionalProperties": false @@ -3746,10 +4360,6 @@ "target-repo": { "type": "string", "description": "Target repository in format 'owner/repo' for cross-repository operations. Takes precedence over trial target repo settings." - }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." } }, "additionalProperties": false, @@ -3758,7 +4368,10 @@ "required-title-prefix": "[refactor] " }, { - "required-labels": ["automated", "stale"], + "required-labels": [ + "automated", + "stale" + ], "max": 10 } ] @@ -3811,7 +4424,10 @@ "required-title-prefix": "[bot] " }, { - "required-labels": ["automated", "outdated"], + "required-labels": [ + "automated", + "outdated" + ], "max": 5 } ] @@ -3842,10 +4458,6 @@ "type": "string", "description": "Target repository in format 'owner/repo' for cross-repository comments. Takes precedence over trial target repo settings." }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." - }, "discussion": { "type": "boolean", "const": true, @@ -3860,7 +4472,13 @@ "description": "List of allowed reasons for hiding older comments when hide-older-comments is enabled. Default: all reasons allowed (spam, abuse, off_topic, outdated, resolved).", "items": { "type": "string", - "enum": ["spam", "abuse", "off_topic", "outdated", "resolved"] + "enum": [ + "spam", + "abuse", + "off_topic", + "outdated", + "resolved" + ] } } }, @@ -3927,7 +4545,11 @@ }, "if-no-changes": { "type": "string", - "enum": ["warn", "error", "ignore"], + "enum": [ + "warn", + "error", + "ignore" + ], "description": "Behavior when no changes to push: 'warn' (default - log warning but succeed), 'error' (fail the action), or 'ignore' (silent success)" }, "allow-empty": { @@ -3962,13 +4584,19 @@ "examples": [ { "title-prefix": "[docs] ", - "labels": ["documentation", "automation"], + "labels": [ + "documentation", + "automation" + ], "reviewers": "copilot", "draft": false }, { "title-prefix": "[security-fix] ", - "labels": ["security", "automated-fix"], + "labels": [ + "security", + "automated-fix" + ], "reviewers": "copilot" } ] @@ -3994,7 +4622,10 @@ "side": { "type": "string", "description": "Side of the diff for comments: 'LEFT' or 'RIGHT' (default: 'RIGHT')", - "enum": ["LEFT", "RIGHT"] + "enum": [ + "LEFT", + "RIGHT" + ] }, "target": { "type": "string", @@ -4216,7 +4847,10 @@ "minimum": 1 }, "target": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Target issue to assign users to. Use 'triggering' (default) for the triggering issue, '*' to allow any issue, or a specific issue number." }, "target-repo": { @@ -4316,10 +4950,6 @@ "target-repo": { "type": "string", "description": "Target repository in format 'owner/repo' for cross-repository issue updates. Takes precedence over trial target repo settings." - }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." } }, "additionalProperties": false @@ -4402,7 +5032,11 @@ }, "if-no-changes": { "type": "string", - "enum": ["warn", "error", "ignore"], + "enum": [ + "warn", + "error", + "ignore" + ], "description": "Behavior when no changes to push: 'warn' (default - log warning but succeed), 'error' (fail the action), or 'ignore' (silent success)" }, "commit-title-suffix": { @@ -4447,7 +5081,13 @@ "description": "List of allowed reasons for hiding comments. Default: all reasons allowed (spam, abuse, off_topic, outdated, resolved).", "items": { "type": "string", - "enum": ["spam", "abuse", "off_topic", "outdated", "resolved"] + "enum": [ + "spam", + "abuse", + "off_topic", + "outdated", + "resolved" + ] } } }, @@ -4593,7 +5233,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "env": { "type": "object", @@ -4609,7 +5252,11 @@ "github-token": { "$ref": "#/$defs/github_token", "description": "GitHub token to use for safe output jobs. Typically a secret reference like ${{ secrets.GITHUB_TOKEN }} or ${{ secrets.CUSTOM_PAT }}", - "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}"] + "examples": [ + "${{ secrets.GITHUB_TOKEN }}", + "${{ secrets.CUSTOM_PAT }}", + "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}" + ] }, "app": { "type": "object", @@ -4618,17 +5265,25 @@ "app-id": { "type": "string", "description": "GitHub App ID. Should reference a variable (e.g., ${{ vars.APP_ID }}).", - "examples": ["${{ vars.APP_ID }}", "${{ secrets.APP_ID }}"] + "examples": [ + "${{ vars.APP_ID }}", + "${{ secrets.APP_ID }}" + ] }, "private-key": { "type": "string", "description": "GitHub App private key. Should reference a secret (e.g., ${{ secrets.APP_PRIVATE_KEY }}).", - "examples": ["${{ secrets.APP_PRIVATE_KEY }}"] + "examples": [ + "${{ secrets.APP_PRIVATE_KEY }}" + ] }, "owner": { "type": "string", "description": "Optional: The owner of the GitHub App installation. If empty, defaults to the current repository owner.", - "examples": ["my-organization", "${{ github.repository_owner }}"] + "examples": [ + "my-organization", + "${{ github.repository_owner }}" + ] }, "repositories": { "type": "array", @@ -4636,10 +5291,21 @@ "items": { "type": "string" }, - "examples": [["repo1", "repo2"], ["my-repo"]] + "examples": [ + [ + "repo1", + "repo2" + ], + [ + "my-repo" + ] + ] } }, - "required": ["app-id", "private-key"], + "required": [ + "app-id", + "private-key" + ], "additionalProperties": false }, "max-patch-size": { @@ -4786,7 +5452,11 @@ }, "type": { "type": "string", - "enum": ["string", "boolean", "choice"], + "enum": [ + "string", + "boolean", + "choice" + ], "description": "Input parameter type", "default": "string" }, @@ -4823,42 +5493,65 @@ "footer": { "type": "string", "description": "Custom footer message template for AI-generated content. Available placeholders: {workflow_name}, {run_url}, {triggering_number}, {workflow_source}, {workflow_source_url}. Example: '> Generated by [{workflow_name}]({run_url})'", - "examples": ["> Generated by [{workflow_name}]({run_url})", "> AI output from [{workflow_name}]({run_url}) for #{triggering_number}"] + "examples": [ + "> Generated by [{workflow_name}]({run_url})", + "> AI output from [{workflow_name}]({run_url}) for #{triggering_number}" + ] }, "footer-install": { "type": "string", "description": "Custom installation instructions template appended to the footer. Available placeholders: {workflow_source}, {workflow_source_url}. Example: '> Install: `gh aw add {workflow_source}`'", - "examples": ["> Install: `gh aw add {workflow_source}`", "> [Add this workflow]({workflow_source_url})"] + "examples": [ + "> Install: `gh aw add {workflow_source}`", + "> [Add this workflow]({workflow_source_url})" + ] }, "staged-title": { "type": "string", "description": "Custom title template for staged mode preview. Available placeholders: {operation}. Example: '\ud83c\udfad Preview: {operation}'", - "examples": ["\ud83c\udfad Preview: {operation}", "## Staged Mode: {operation}"] + "examples": [ + "\ud83c\udfad Preview: {operation}", + "## Staged Mode: {operation}" + ] }, "staged-description": { "type": "string", "description": "Custom description template for staged mode preview. Available placeholders: {operation}. Example: 'The following {operation} would occur if staged mode was disabled:'", - "examples": ["The following {operation} would occur if staged mode was disabled:"] + "examples": [ + "The following {operation} would occur if staged mode was disabled:" + ] }, "run-started": { "type": "string", "description": "Custom message template for workflow activation comment. Available placeholders: {workflow_name}, {run_url}, {event_type}. Default: 'Agentic [{workflow_name}]({run_url}) triggered by this {event_type}.'", - "examples": ["Agentic [{workflow_name}]({run_url}) triggered by this {event_type}.", "[{workflow_name}]({run_url}) started processing this {event_type}."] + "examples": [ + "Agentic [{workflow_name}]({run_url}) triggered by this {event_type}.", + "[{workflow_name}]({run_url}) started processing this {event_type}." + ] }, "run-success": { "type": "string", "description": "Custom message template for successful workflow completion. Available placeholders: {workflow_name}, {run_url}. Default: '\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.'", - "examples": ["\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.", "\u2705 [{workflow_name}]({run_url}) finished."] + "examples": [ + "\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.", + "\u2705 [{workflow_name}]({run_url}) finished." + ] }, "run-failure": { "type": "string", "description": "Custom message template for failed workflow. Available placeholders: {workflow_name}, {run_url}, {status}. Default: '\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'", - "examples": ["\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", "\u274c [{workflow_name}]({run_url}) {status}."] + "examples": [ + "\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", + "\u274c [{workflow_name}]({run_url}) {status}." + ] }, "detection-failure": { "type": "string", "description": "Custom message template for detection job failure. Available placeholders: {workflow_name}, {run_url}. Default: '\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.'", - "examples": ["\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", "\u26a0\ufe0f Detection job failed in [{workflow_name}]({run_url})."] + "examples": [ + "\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", + "\u26a0\ufe0f Detection job failed in [{workflow_name}]({run_url})." + ] } }, "additionalProperties": false @@ -4937,7 +5630,9 @@ "oneOf": [ { "type": "string", - "enum": ["all"], + "enum": [ + "all" + ], "description": "Allow any authenticated user to trigger the workflow (\u26a0\ufe0f disables permission checking entirely - use with caution)" }, { @@ -4945,7 +5640,13 @@ "description": "List of repository permission levels that can trigger the workflow. Permission checks are automatically applied to potentially unsafe triggers.", "items": { "type": "string", - "enum": ["admin", "maintainer", "maintain", "write", "triage"], + "enum": [ + "admin", + "maintainer", + "maintain", + "write", + "triage" + ], "description": "Repository permission level: 'admin' (full access), 'maintainer'/'maintain' (repository management), 'write' (push access), 'triage' (issue management)" }, "minItems": 1 @@ -5020,10 +5721,14 @@ "additionalProperties": false, "anyOf": [ { - "required": ["uses"] + "required": [ + "uses" + ] }, { - "required": ["run"] + "required": [ + "run" + ] } ] }, @@ -5032,7 +5737,10 @@ "default": true, "$comment": "Strict mode enforces several security constraints that are validated in Go code (pkg/workflow/strict_mode_validation.go) rather than JSON Schema: (1) Write Permissions + Safe Outputs: When strict=true AND permissions contains write values (contents:write, issues:write, pull-requests:write), safe-outputs must be configured. This relationship is too complex for JSON Schema as it requires checking if ANY permission property has a 'write' value. (2) Network Requirements: When strict=true, the 'network' field must be present and cannot contain wildcard '*'. (3) MCP Container Network: Custom MCP servers with containers require explicit network configuration. (4) Action Pinning: Actions must be pinned to commit SHAs. These are enforced during compilation via validateStrictMode().", "description": "Enable strict mode validation for enhanced security and compliance. Strict mode enforces: (1) Write Permissions - refuses contents:write, issues:write, pull-requests:write; requires safe-outputs instead, (2) Network Configuration - requires explicit network configuration with no wildcard '*' in allowed domains, (3) Action Pinning - enforces actions pinned to commit SHAs instead of tags/branches, (4) MCP Network - requires network configuration for custom MCP servers with containers, (5) Deprecated Fields - refuses deprecated frontmatter fields. Can be enabled per-workflow via 'strict: true' in frontmatter, or disabled via 'strict: false'. CLI flag takes precedence over frontmatter (gh aw compile --strict enforces strict mode). Defaults to true. See: https://githubnext.github.io/gh-aw/reference/frontmatter/#strict-mode-strict", - "examples": [true, false] + "examples": [ + true, + false + ] }, "safe-inputs": { "type": "object", @@ -5041,7 +5749,9 @@ "^([a-ln-z][a-z0-9_-]*|m[a-np-z][a-z0-9_-]*|mo[a-ce-z][a-z0-9_-]*|mod[a-df-z][a-z0-9_-]*|mode[a-z0-9_-]+)$": { "type": "object", "description": "Custom tool definition. The key is the tool name (lowercase alphanumeric with dashes/underscores).", - "required": ["description"], + "required": [ + "description" + ], "properties": { "description": { "type": "string", @@ -5055,7 +5765,13 @@ "properties": { "type": { "type": "string", - "enum": ["string", "number", "boolean", "array", "object"], + "enum": [ + "string", + "number", + "boolean", + "array", + "object" + ], "default": "string", "description": "The JSON schema type of the input parameter." }, @@ -5105,46 +5821,69 @@ "description": "Timeout in seconds for tool execution. Default is 60 seconds. Applies to shell (run) and Python (py) tools.", "default": 60, "minimum": 1, - "examples": [30, 60, 120, 300] + "examples": [ + 30, + 60, + 120, + 300 + ] } }, "additionalProperties": false, "oneOf": [ { - "required": ["script"], + "required": [ + "script" + ], "not": { "anyOf": [ { - "required": ["run"] + "required": [ + "run" + ] }, { - "required": ["py"] + "required": [ + "py" + ] } ] } }, { - "required": ["run"], + "required": [ + "run" + ], "not": { "anyOf": [ { - "required": ["script"] + "required": [ + "script" + ] }, { - "required": ["py"] + "required": [ + "py" + ] } ] } }, { - "required": ["py"], + "required": [ + "py" + ], "not": { "anyOf": [ { - "required": ["script"] + "required": [ + "script" + ] }, { - "required": ["run"] + "required": [ + "run" + ] } ] } @@ -5194,7 +5933,9 @@ "properties": { "mode": { "type": "string", - "enum": ["http"], + "enum": [ + "http" + ], "default": "http", "description": "Deprecated: Transport mode for the safe-inputs MCP server. This field is ignored as only 'http' mode is supported. The server always starts as a separate step.", "deprecated": true, @@ -5211,9 +5952,18 @@ "description": "Runtime configuration object identified by runtime ID (e.g., 'node', 'python', 'go')", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Runtime version as a string (e.g., '22', '3.12', 'latest') or number (e.g., 22, 3.12). Numeric values are automatically converted to strings at runtime.", - "examples": ["22", "3.12", "latest", 22, 3.12] + "examples": [ + "22", + "3.12", + "latest", + 22, + 3.12 + ] }, "action-repo": { "type": "string", @@ -5250,7 +6000,9 @@ } } }, - "required": ["slash_command"] + "required": [ + "slash_command" + ] }, { "properties": { @@ -5260,7 +6012,9 @@ } } }, - "required": ["command"] + "required": [ + "command" + ] } ] } @@ -5279,7 +6033,9 @@ } } }, - "required": ["issue_comment"] + "required": [ + "issue_comment" + ] }, { "properties": { @@ -5289,7 +6045,9 @@ } } }, - "required": ["pull_request_review_comment"] + "required": [ + "pull_request_review_comment" + ] }, { "properties": { @@ -5299,7 +6057,9 @@ } } }, - "required": ["label"] + "required": [ + "label" + ] } ] } @@ -5333,7 +6093,12 @@ "oneOf": [ { "type": "string", - "enum": ["claude", "codex", "copilot", "custom"], + "enum": [ + "claude", + "codex", + "copilot", + "custom" + ], "description": "Simple engine name: 'claude' (default, Claude Code), 'copilot' (GitHub Copilot CLI), 'codex' (OpenAI Codex CLI), or 'custom' (user-defined steps)" }, { @@ -5342,13 +6107,26 @@ "properties": { "id": { "type": "string", - "enum": ["claude", "codex", "custom", "copilot"], + "enum": [ + "claude", + "codex", + "custom", + "copilot" + ], "description": "AI engine identifier: 'claude' (Claude Code), 'codex' (OpenAI Codex CLI), 'copilot' (GitHub Copilot CLI), or 'custom' (user-defined GitHub Actions steps)" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version of the AI engine action (e.g., 'beta', 'stable', 20). Has sensible defaults and can typically be omitted. Numeric values are automatically converted to strings at runtime.", - "examples": ["beta", "stable", 20, 3.11] + "examples": [ + "beta", + "stable", + 20, + 3.11 + ] }, "model": { "type": "string", @@ -5386,7 +6164,9 @@ "description": "Whether to cancel in-progress runs of the same concurrency group. Defaults to false for agentic workflow runs." } }, - "required": ["group"], + "required": [ + "group" + ], "additionalProperties": false } ], @@ -5441,7 +6221,9 @@ "description": "Human-readable description of what this pattern matches" } }, - "required": ["pattern"], + "required": [ + "pattern" + ], "additionalProperties": false } }, @@ -5457,7 +6239,9 @@ "description": "Optional array of command-line arguments to pass to the AI engine CLI. These arguments are injected after all other args but before the prompt." } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false } ] @@ -5468,7 +6252,10 @@ "properties": { "type": { "type": "string", - "enum": ["stdio", "local"], + "enum": [ + "stdio", + "local" + ], "description": "MCP connection type for stdio (local is an alias for stdio)" }, "registry": { @@ -5488,9 +6275,17 @@ "description": "Container image for stdio MCP connections" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version/tag for the container image (e.g., 'latest', 'v1.0.0', 20, 3.11). Numeric values are automatically converted to strings at runtime.", - "examples": ["latest", "v1.0.0", 20, 3.11] + "examples": [ + "latest", + "v1.0.0", + 20, + 3.11 + ] }, "args": { "type": "array", @@ -5554,49 +6349,70 @@ "$comment": "Validation constraints: (1) Mutual exclusion: 'command' and 'container' cannot both be specified. (2) Requirement: Either 'command' or 'container' must be provided (via 'anyOf'). (3) Dependency: 'network' requires 'container' (validated in 'allOf'). (4) Type constraint: When 'type' is 'stdio' or 'local', either 'command' or 'container' is required.", "anyOf": [ { - "required": ["type"] + "required": [ + "type" + ] }, { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ], "not": { "allOf": [ { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ] }, "allOf": [ { "if": { - "required": ["network"] + "required": [ + "network" + ] }, "then": { - "required": ["container"] + "required": [ + "container" + ] } }, { "if": { "properties": { "type": { - "enum": ["stdio", "local"] + "enum": [ + "stdio", + "local" + ] } } }, "then": { "anyOf": [ { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ] } @@ -5609,7 +6425,9 @@ "properties": { "type": { "type": "string", - "enum": ["http"], + "enum": [ + "http" + ], "description": "MCP connection type for HTTP" }, "registry": { @@ -5639,14 +6457,20 @@ } } }, - "required": ["url"], + "required": [ + "url" + ], "additionalProperties": false }, "github_token": { "type": "string", "pattern": "^\\$\\{\\{\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*\\s*\\}\\}$", "description": "GitHub token expression using secrets. Pattern details: `[A-Za-z_][A-Za-z0-9_]*` matches a valid secret name (starts with a letter or underscore, followed by letters, digits, or underscores). The full pattern matches expressions like `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`.", - "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}"] + "examples": [ + "${{ secrets.GITHUB_TOKEN }}", + "${{ secrets.CUSTOM_PAT }}", + "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}" + ] } } -} +} \ No newline at end of file diff --git a/pkg/workflow/compiler_safe_outputs_core.go b/pkg/workflow/compiler_safe_outputs_core.go index b11e62dac4..37c91e48ec 100644 --- a/pkg/workflow/compiler_safe_outputs_core.go +++ b/pkg/workflow/compiler_safe_outputs_core.go @@ -1,6 +1,7 @@ package workflow import ( + "encoding/json" "fmt" "github.com/githubnext/gh-aw/pkg/constants" @@ -52,7 +53,6 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa // Track which outputs are created for dependency tracking var createIssueEnabled bool - var createDiscussionEnabled bool var createPullRequestEnabled bool // Add GitHub App token minting step if app is configured @@ -100,55 +100,65 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa prCheckoutStepsAdded = true } - // === Build individual safe output steps === - - // 1. Create Issue step - if data.SafeOutputs.CreateIssues != nil { - createIssueEnabled = true - stepConfig := c.buildCreateIssueStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - // Add outputs - outputs["create_issue_issue_number"] = "${{ steps.create_issue.outputs.issue_number }}" - outputs["create_issue_issue_url"] = "${{ steps.create_issue.outputs.issue_url }}" - outputs["create_issue_temporary_id_map"] = "${{ steps.create_issue.outputs.temporary_id_map }}" - - // Merge permissions - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - - // 2. Create Discussion step - if data.SafeOutputs.CreateDiscussions != nil { - createDiscussionEnabled = true - stepConfig := c.buildCreateDiscussionStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - outputs["create_discussion_discussion_number"] = "${{ steps.create_discussion.outputs.discussion_number }}" - outputs["create_discussion_discussion_url"] = "${{ steps.create_discussion.outputs.discussion_url }}" - - permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) - } - - // 2a. Update Discussion step - if data.SafeOutputs.UpdateDiscussions != nil { - stepConfig := c.buildUpdateDiscussionStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) + // === Build safe output steps === + + // Check if any handler-manager-supported types are enabled + hasHandlerManagerTypes := data.SafeOutputs.CreateIssues != nil || + data.SafeOutputs.AddComments != nil || + data.SafeOutputs.CreateDiscussions != nil || + data.SafeOutputs.CloseIssues != nil || + data.SafeOutputs.CloseDiscussions != nil || + data.SafeOutputs.AddLabels != nil || + data.SafeOutputs.UpdateIssues != nil || + data.SafeOutputs.UpdateDiscussions != nil + + // If we have handler manager types, use the handler manager step + if hasHandlerManagerTypes { + consolidatedSafeOutputsLog.Print("Using handler manager for safe outputs") + handlerManagerSteps := c.buildHandlerManagerStep(data) + steps = append(steps, handlerManagerSteps...) + safeOutputStepNames = append(safeOutputStepNames, "process_safe_outputs") + + // Track enabled types for other steps + if data.SafeOutputs.CreateIssues != nil { + createIssueEnabled = true + } - outputs["update_discussion_discussion_number"] = "${{ steps.update_discussion.outputs.discussion_number }}" - outputs["update_discussion_discussion_url"] = "${{ steps.update_discussion.outputs.discussion_url }}" + // Add outputs from handler manager + outputs["process_safe_outputs_temporary_id_map"] = "${{ steps.process_safe_outputs.outputs.temporary_id_map }}" + outputs["process_safe_outputs_processed_count"] = "${{ steps.process_safe_outputs.outputs.processed_count }}" - permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) + // Merge permissions for all handler-managed types + if data.SafeOutputs.CreateIssues != nil { + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if data.SafeOutputs.CreateDiscussions != nil { + permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) + } + if data.SafeOutputs.AddComments != nil { + permissions.Merge(NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()) + } + if data.SafeOutputs.CloseIssues != nil { + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if data.SafeOutputs.CloseDiscussions != nil { + permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) + } + if data.SafeOutputs.AddLabels != nil { + permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) + } + if data.SafeOutputs.UpdateIssues != nil { + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if data.SafeOutputs.UpdateDiscussions != nil { + permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) + } } - // 3. Create Pull Request step + // Create Pull Request step (not handled by handler manager) if data.SafeOutputs.CreatePullRequests != nil { createPullRequestEnabled = true + _ = createPullRequestEnabled // Track for potential future use stepConfig := c.buildCreatePullRequestStepConfig(data, mainJobName, threatDetectionEnabled) // Skip pre-steps if we've already added the shared checkout steps if !prCheckoutStepsAdded { @@ -165,41 +175,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa permissions.Merge(NewPermissionsContentsWriteIssuesWritePRWrite()) } - // 4. Add Comment step (needs to come after create_issue, create_discussion, create_pull_request) - if data.SafeOutputs.AddComments != nil { - stepConfig := c.buildAddCommentStepConfig(data, mainJobName, threatDetectionEnabled, - createIssueEnabled, createDiscussionEnabled, createPullRequestEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - outputs["add_comment_comment_id"] = "${{ steps.add_comment.outputs.comment_id }}" - outputs["add_comment_comment_url"] = "${{ steps.add_comment.outputs.comment_url }}" - - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()) - } - - // 5. Close Discussion step - if data.SafeOutputs.CloseDiscussions != nil { - stepConfig := c.buildCloseDiscussionStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) - } - - // 6. Close Issue step - if data.SafeOutputs.CloseIssues != nil { - stepConfig := c.buildCloseIssueStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - - // 7. Close Pull Request step + // Close Pull Request step (not handled by handler manager) if data.SafeOutputs.ClosePullRequests != nil { stepConfig := c.buildClosePullRequestStepConfig(data, mainJobName, threatDetectionEnabled) stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) @@ -209,7 +185,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa permissions.Merge(NewPermissionsContentsReadPRWrite()) } - // 8. Create PR Review Comment step + // Create PR Review Comment step if data.SafeOutputs.CreatePullRequestReviewComments != nil { stepConfig := c.buildCreatePRReviewCommentStepConfig(data, mainJobName, threatDetectionEnabled) stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) @@ -230,18 +206,6 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa permissions.Merge(NewPermissionsContentsReadSecurityEventsWrite()) } - // 10. Add Labels step - if data.SafeOutputs.AddLabels != nil { - stepConfig := c.buildAddLabelsStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - outputs["add_labels_labels_added"] = "${{ steps.add_labels.outputs.labels_added }}" - - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) - } - // 11. Add Reviewer step if data.SafeOutputs.AddReviewer != nil { stepConfig := c.buildAddReviewerStepConfig(data, mainJobName, threatDetectionEnabled) @@ -290,16 +254,6 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) } - // 15. Update Issue step - if data.SafeOutputs.UpdateIssues != nil { - stepConfig := c.buildUpdateIssueStepConfig(data, mainJobName, threatDetectionEnabled) - stepYAML := c.buildConsolidatedSafeOutputStep(data, stepConfig) - steps = append(steps, stepYAML...) - safeOutputStepNames = append(safeOutputStepNames, stepConfig.StepID) - - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - // 16. Update Pull Request step if data.SafeOutputs.UpdatePullRequests != nil { stepConfig := c.buildUpdatePullRequestStepConfig(data, mainJobName, threatDetectionEnabled) @@ -679,3 +633,315 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string { consolidatedSafeOutputsLog.Printf("Added shared checkout with condition: %s", condition.Render()) return steps } + +// buildHandlerManagerStep builds a single step that uses the safe output handler manager +// to dispatch messages to appropriate handlers. This replaces multiple individual steps +// with a single dispatcher step. +func (c *Compiler) buildHandlerManagerStep(data *WorkflowData) []string { + consolidatedSafeOutputsLog.Print("Building handler manager step") + + var steps []string + + // Step name and metadata + steps = append(steps, " - name: Process Safe Outputs\n") + steps = append(steps, " id: process_safe_outputs\n") + steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script"))) + + // Environment variables + steps = append(steps, " env:\n") + steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}\n") + + // Add custom safe output env vars + c.addCustomSafeOutputEnvVars(&steps, data) + + // Add handler manager config as JSON + c.addHandlerManagerConfigEnvVar(&steps, data) + + // Add all safe output configuration env vars (still needed by individual handlers) + c.addAllSafeOutputConfigEnvVars(&steps, data) + + // With section for github-token + steps = append(steps, " with:\n") + c.addSafeOutputGitHubTokenForConfig(&steps, data, "") + + steps = append(steps, " script: |\n") + steps = append(steps, " const { setupGlobals } = require('"+SetupActionDestination+"/setup_globals.cjs');\n") + steps = append(steps, " setupGlobals(core, github, context, exec, io);\n") + steps = append(steps, " const { main } = require('"+SetupActionDestination+"/safe_output_handler_manager.cjs');\n") + steps = append(steps, " await main();\n") + + return steps +} + +// addHandlerManagerConfigEnvVar adds a JSON config environment variable for the handler manager +// This config indicates which handlers should be loaded and includes their type-specific options +// The presence of a config key indicates that handler is enabled (no explicit "enabled" field needed) +func (c *Compiler) addHandlerManagerConfigEnvVar(steps *[]string, data *WorkflowData) { + if data.SafeOutputs == nil { + return + } + + config := make(map[string]map[string]any) + + // Add config for each enabled safe output type with their options + // Presence in config = enabled, so no need for "enabled": true field + if data.SafeOutputs.CreateIssues != nil { + cfg := data.SafeOutputs.CreateIssues + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if len(cfg.AllowedLabels) > 0 { + handlerConfig["allowed_labels"] = cfg.AllowedLabels + } + if len(cfg.AllowedRepos) > 0 { + handlerConfig["allowed_repos"] = cfg.AllowedRepos + } + if cfg.Expires > 0 { + handlerConfig["expires"] = cfg.Expires + } + // Add labels, title_prefix to config + if len(cfg.Labels) > 0 { + handlerConfig["labels"] = cfg.Labels + } + if cfg.TitlePrefix != "" { + handlerConfig["title_prefix"] = cfg.TitlePrefix + } + config["create_issue"] = handlerConfig + } + + if data.SafeOutputs.AddComments != nil { + cfg := data.SafeOutputs.AddComments + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if cfg.Target != "" { + handlerConfig["target"] = cfg.Target + } + if cfg.HideOlderComments { + handlerConfig["hide_older_comments"] = true + } + config["add_comment"] = handlerConfig + } + + if data.SafeOutputs.CreateDiscussions != nil { + cfg := data.SafeOutputs.CreateDiscussions + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if cfg.Category != "" { + handlerConfig["category"] = cfg.Category + } + if cfg.TitlePrefix != "" { + handlerConfig["title_prefix"] = cfg.TitlePrefix + } + if len(cfg.Labels) > 0 { + handlerConfig["labels"] = cfg.Labels + } + if len(cfg.AllowedLabels) > 0 { + handlerConfig["allowed_labels"] = cfg.AllowedLabels + } + if len(cfg.AllowedRepos) > 0 { + handlerConfig["allowed_repos"] = cfg.AllowedRepos + } + if cfg.CloseOlderDiscussions { + handlerConfig["close_older_discussions"] = true + } + if cfg.Expires > 0 { + handlerConfig["expires"] = cfg.Expires + } + config["create_discussion"] = handlerConfig + } + + if data.SafeOutputs.CloseIssues != nil { + cfg := data.SafeOutputs.CloseIssues + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if cfg.Target != "" { + handlerConfig["target"] = cfg.Target + } + if len(cfg.RequiredLabels) > 0 { + handlerConfig["required_labels"] = cfg.RequiredLabels + } + if cfg.RequiredTitlePrefix != "" { + handlerConfig["required_title_prefix"] = cfg.RequiredTitlePrefix + } + config["close_issue"] = handlerConfig + } + + if data.SafeOutputs.CloseDiscussions != nil { + cfg := data.SafeOutputs.CloseDiscussions + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if cfg.Target != "" { + handlerConfig["target"] = cfg.Target + } + if len(cfg.RequiredLabels) > 0 { + handlerConfig["required_labels"] = cfg.RequiredLabels + } + if cfg.RequiredTitlePrefix != "" { + handlerConfig["required_title_prefix"] = cfg.RequiredTitlePrefix + } + if cfg.RequiredCategory != "" { + handlerConfig["required_category"] = cfg.RequiredCategory + } + config["close_discussion"] = handlerConfig + } + + if data.SafeOutputs.AddLabels != nil { + cfg := data.SafeOutputs.AddLabels + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if len(cfg.Allowed) > 0 { + handlerConfig["allowed"] = cfg.Allowed + } + if cfg.Target != "" { + handlerConfig["target"] = cfg.Target + } + config["add_labels"] = handlerConfig + } + + if data.SafeOutputs.UpdateIssues != nil { + cfg := data.SafeOutputs.UpdateIssues + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if cfg.Target != "" { + handlerConfig["target"] = cfg.Target + } + // Boolean pointer fields indicate which fields can be updated + if cfg.Status != nil { + handlerConfig["allow_status"] = true + } + if cfg.Title != nil { + handlerConfig["allow_title"] = true + } + if cfg.Body != nil { + handlerConfig["allow_body"] = true + } + config["update_issue"] = handlerConfig + } + + if data.SafeOutputs.UpdateDiscussions != nil { + cfg := data.SafeOutputs.UpdateDiscussions + handlerConfig := make(map[string]any) + if cfg.Max > 0 { + handlerConfig["max"] = cfg.Max + } + if cfg.Target != "" { + handlerConfig["target"] = cfg.Target + } + // Boolean pointer fields indicate which fields can be updated + if cfg.Title != nil { + handlerConfig["allow_title"] = true + } + if cfg.Body != nil { + handlerConfig["allow_body"] = true + } + if cfg.Labels != nil { + handlerConfig["allow_labels"] = true + } + if len(cfg.AllowedLabels) > 0 { + handlerConfig["allowed_labels"] = cfg.AllowedLabels + } + config["update_discussion"] = handlerConfig + } + + // Only add the env var if there are handlers to configure + if len(config) > 0 { + configJSON, err := json.Marshal(config) + if err != nil { + consolidatedSafeOutputsLog.Printf("Failed to marshal handler config: %v", err) + return + } + // Escape the JSON for YAML (handle quotes and special chars) + configStr := string(configJSON) + *steps = append(*steps, fmt.Sprintf(" GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: %q\n", configStr)) + } +} + +// addAllSafeOutputConfigEnvVars adds environment variables for all enabled safe output types +// These are needed by individual handlers when called by the handler manager +func (c *Compiler) addAllSafeOutputConfigEnvVars(steps *[]string, data *WorkflowData) { + if data.SafeOutputs == nil { + return + } + + // Track if we've already added staged flag to avoid duplicates + stagedFlagAdded := false + + // Create Issue env vars - allowed_labels and allowed_repos now in config object + if data.SafeOutputs.CreateIssues != nil { + cfg := data.SafeOutputs.CreateIssues + // Add target repo slug if specified + if cfg.TargetRepoSlug != "" { + *steps = append(*steps, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", cfg.TargetRepoSlug)) + } else if !c.trialMode && data.SafeOutputs.Staged && !stagedFlagAdded { + *steps = append(*steps, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n") + stagedFlagAdded = true + } + } + + // Add Comment - all config now in handler config JSON + if data.SafeOutputs.AddComments != nil { + cfg := data.SafeOutputs.AddComments + // Add target repo slug if specified + if cfg.TargetRepoSlug != "" { + *steps = append(*steps, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", cfg.TargetRepoSlug)) + } else if !c.trialMode && data.SafeOutputs.Staged && !stagedFlagAdded { + *steps = append(*steps, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n") + stagedFlagAdded = true + } + // All add_comment configuration (target, hide_older_comments, max) is now in handler config JSON + } + + // Add Labels - all config now in handler config JSON + if data.SafeOutputs.AddLabels != nil { + cfg := data.SafeOutputs.AddLabels + // Add target repo slug if specified + if cfg.TargetRepoSlug != "" { + *steps = append(*steps, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", cfg.TargetRepoSlug)) + } else if !c.trialMode && data.SafeOutputs.Staged && !stagedFlagAdded { + *steps = append(*steps, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n") + stagedFlagAdded = true + } + // All add_labels configuration (allowed, max, target) is now in handler config JSON + } + + // Update Issue env vars + if data.SafeOutputs.UpdateIssues != nil { + cfg := data.SafeOutputs.UpdateIssues + // Add target repo slug if specified + if cfg.TargetRepoSlug != "" { + *steps = append(*steps, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", cfg.TargetRepoSlug)) + } else if !c.trialMode && data.SafeOutputs.Staged && !stagedFlagAdded { + *steps = append(*steps, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n") + stagedFlagAdded = true + } + } + + // Update Discussion env vars + if data.SafeOutputs.UpdateDiscussions != nil { + cfg := data.SafeOutputs.UpdateDiscussions + // Add target repo slug if specified + if cfg.TargetRepoSlug != "" { + *steps = append(*steps, fmt.Sprintf(" GH_AW_TARGET_REPO_SLUG: %q\n", cfg.TargetRepoSlug)) + } else if !c.trialMode && data.SafeOutputs.Staged && !stagedFlagAdded { + *steps = append(*steps, " GH_AW_SAFE_OUTPUTS_STAGED: \"true\"\n") + stagedFlagAdded = true + _ = stagedFlagAdded // Mark as used for linter + } + // All update configuration (target, allow_title, allow_body, allow_labels) is now in handler config JSON + } + + // Note: Most handlers read from the config.json file, so we may not need all env vars here +} diff --git a/pkg/workflow/compiler_safe_outputs_discussions.go b/pkg/workflow/compiler_safe_outputs_discussions.go index 78463125f2..ffaa8a2b21 100644 --- a/pkg/workflow/compiler_safe_outputs_discussions.go +++ b/pkg/workflow/compiler_safe_outputs_discussions.go @@ -1,79 +1,6 @@ package workflow -import "fmt" - -// buildCreateDiscussionStepConfig builds the configuration for creating a discussion -func (c *Compiler) buildCreateDiscussionStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.CreateDiscussions - - var customEnvVars []string - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, "")...) - - condition := BuildSafeOutputType("create_discussion") - - return SafeOutputStepConfig{ - StepName: "Create Discussion", - StepID: "create_discussion", - ScriptName: "create_discussion", - Script: getCreateDiscussionScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - -// buildCloseDiscussionStepConfig builds the configuration for closing a discussion -func (c *Compiler) buildCloseDiscussionStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.CloseDiscussions - - var customEnvVars []string - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, "")...) - - condition := BuildSafeOutputType("close_discussion") - - return SafeOutputStepConfig{ - StepName: "Close Discussion", - StepID: "close_discussion", - ScriptName: "close_discussion", - Script: getCloseDiscussionScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - -// buildUpdateDiscussionStepConfig builds the configuration for updating a discussion -func (c *Compiler) buildUpdateDiscussionStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.UpdateDiscussions - - var customEnvVars []string - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, cfg.TargetRepoSlug)...) - - // Add target environment variable if set - if cfg.Target != "" { - customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_UPDATE_TARGET: %q\n", cfg.Target)) - } - - // Add field update flags - presence of pointer indicates field can be updated - if cfg.Title != nil { - customEnvVars = append(customEnvVars, " GH_AW_UPDATE_TITLE: \"true\"\n") - } - if cfg.Body != nil { - customEnvVars = append(customEnvVars, " GH_AW_UPDATE_BODY: \"true\"\n") - } - if cfg.Labels != nil { - customEnvVars = append(customEnvVars, " GH_AW_UPDATE_LABELS: \"true\"\n") - } - - condition := BuildSafeOutputType("update_discussion") - - return SafeOutputStepConfig{ - StepName: "Update Discussion", - StepID: "update_discussion", - ScriptName: "update_discussion", - Script: getUpdateDiscussionScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} +// This file previously contained discussion-related step config builders. +// Those functions have been removed as discussions are now handled by the +// safe output handler manager (safe_output_handler_manager.cjs). +// See pkg/workflow/compiler_safe_outputs_core.go for the new implementation. diff --git a/pkg/workflow/compiler_safe_outputs_issues.go b/pkg/workflow/compiler_safe_outputs_issues.go index ddeeaffcdd..74b293f659 100644 --- a/pkg/workflow/compiler_safe_outputs_issues.go +++ b/pkg/workflow/compiler_safe_outputs_issues.go @@ -1,74 +1,5 @@ package workflow -import "fmt" - -// buildCreateIssueStepConfig builds the configuration for creating an issue -func (c *Compiler) buildCreateIssueStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.CreateIssues - - var customEnvVars []string - customEnvVars = append(customEnvVars, buildTitlePrefixEnvVar("GH_AW_ISSUE_TITLE_PREFIX", cfg.TitlePrefix)...) - customEnvVars = append(customEnvVars, buildLabelsEnvVar("GH_AW_ISSUE_LABELS", cfg.Labels)...) - customEnvVars = append(customEnvVars, buildLabelsEnvVar("GH_AW_ISSUE_ALLOWED_LABELS", cfg.AllowedLabels)...) - customEnvVars = append(customEnvVars, buildAllowedReposEnvVar("GH_AW_ALLOWED_REPOS", cfg.AllowedRepos)...) - if cfg.Expires > 0 { - customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_ISSUE_EXPIRES: \"%d\"\n", cfg.Expires)) - } - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, cfg.TargetRepoSlug)...) - - condition := BuildSafeOutputType("create_issue") - - return SafeOutputStepConfig{ - StepName: "Create Issue", - StepID: "create_issue", - ScriptName: "create_issue", - Script: getCreateIssueScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - -// buildCloseIssueStepConfig builds the configuration for closing an issue -func (c *Compiler) buildCloseIssueStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.CloseIssues - - var customEnvVars []string - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, "")...) - - condition := BuildSafeOutputType("close_issue") - - return SafeOutputStepConfig{ - StepName: "Close Issue", - StepID: "close_issue", - ScriptName: "close_issue", - Script: getCloseIssueScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - -// buildUpdateIssueStepConfig builds the configuration for updating an issue -func (c *Compiler) buildUpdateIssueStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.UpdateIssues - - var customEnvVars []string - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, cfg.TargetRepoSlug)...) - - condition := BuildSafeOutputType("update_issue") - - return SafeOutputStepConfig{ - StepName: "Update Issue", - StepID: "update_issue", - ScriptName: "update_issue", - Script: getUpdateIssueScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - // buildLinkSubIssueStepConfig builds the configuration for linking a sub-issue func (c *Compiler) buildLinkSubIssueStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool, createIssueEnabled bool) SafeOutputStepConfig { cfg := data.SafeOutputs.LinkSubIssue diff --git a/pkg/workflow/compiler_safe_outputs_shared.go b/pkg/workflow/compiler_safe_outputs_shared.go index 238e077ca1..692dc707b4 100644 --- a/pkg/workflow/compiler_safe_outputs_shared.go +++ b/pkg/workflow/compiler_safe_outputs_shared.go @@ -1,79 +1,5 @@ package workflow -import "fmt" - -// buildAddCommentStepConfig builds the configuration for adding a comment -// This works across multiple entity types (issues, PRs, discussions) -func (c *Compiler) buildAddCommentStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool, createIssueEnabled, createDiscussionEnabled, createPullRequestEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.AddComments - - var customEnvVars []string - if cfg.Target != "" { - customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_COMMENT_TARGET: %q\n", cfg.Target)) - } - if cfg.Discussion != nil && *cfg.Discussion { - customEnvVars = append(customEnvVars, " GITHUB_AW_COMMENT_DISCUSSION: \"true\"\n") - } - if cfg.HideOlderComments { - customEnvVars = append(customEnvVars, " GH_AW_HIDE_OLDER_COMMENTS: \"true\"\n") - } - - // Reference outputs from earlier steps in the same job - if createIssueEnabled { - customEnvVars = append(customEnvVars, " GH_AW_CREATED_ISSUE_URL: ${{ steps.create_issue.outputs.issue_url }}\n") - customEnvVars = append(customEnvVars, " GH_AW_CREATED_ISSUE_NUMBER: ${{ steps.create_issue.outputs.issue_number }}\n") - customEnvVars = append(customEnvVars, " GH_AW_TEMPORARY_ID_MAP: ${{ steps.create_issue.outputs.temporary_id_map }}\n") - } - if createDiscussionEnabled { - customEnvVars = append(customEnvVars, " GH_AW_CREATED_DISCUSSION_URL: ${{ steps.create_discussion.outputs.discussion_url }}\n") - customEnvVars = append(customEnvVars, " GH_AW_CREATED_DISCUSSION_NUMBER: ${{ steps.create_discussion.outputs.discussion_number }}\n") - } - if createPullRequestEnabled { - customEnvVars = append(customEnvVars, " GH_AW_CREATED_PULL_REQUEST_URL: ${{ steps.create_pull_request.outputs.pull_request_url }}\n") - customEnvVars = append(customEnvVars, " GH_AW_CREATED_PULL_REQUEST_NUMBER: ${{ steps.create_pull_request.outputs.pull_request_number }}\n") - } - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, cfg.TargetRepoSlug)...) - - condition := BuildSafeOutputType("add_comment") - - return SafeOutputStepConfig{ - StepName: "Add Comment", - StepID: "add_comment", - ScriptName: "add_comment", - Script: getAddCommentScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - -// buildAddLabelsStepConfig builds the configuration for adding labels -func (c *Compiler) buildAddLabelsStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { - cfg := data.SafeOutputs.AddLabels - - var customEnvVars []string - customEnvVars = append(customEnvVars, buildLabelsEnvVar("GH_AW_LABELS_ALLOWED", cfg.Allowed)...) - if cfg.Max > 0 { - customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_LABELS_MAX_COUNT: %d\n", cfg.Max)) - } - if cfg.Target != "" { - customEnvVars = append(customEnvVars, fmt.Sprintf(" GH_AW_LABELS_TARGET: %q\n", cfg.Target)) - } - customEnvVars = append(customEnvVars, c.buildStepLevelSafeOutputEnvVars(data, cfg.TargetRepoSlug)...) - - condition := BuildSafeOutputType("add_labels") - - return SafeOutputStepConfig{ - StepName: "Add Labels", - StepID: "add_labels", - ScriptName: "add_labels", - Script: getAddLabelsScript(), - CustomEnvVars: customEnvVars, - Condition: condition, - Token: cfg.GitHubToken, - } -} - // buildHideCommentStepConfig builds the configuration for hiding a comment func (c *Compiler) buildHideCommentStepConfig(data *WorkflowData, mainJobName string, threatDetectionEnabled bool) SafeOutputStepConfig { cfg := data.SafeOutputs.HideComment diff --git a/pkg/workflow/js.go b/pkg/workflow/js.go index 76aa830184..c2d98dd51b 100644 --- a/pkg/workflow/js.go +++ b/pkg/workflow/js.go @@ -39,8 +39,6 @@ func getLinkSubIssueScript() string { return "" } func getNoOpScript() string { return "" } func getNotifyCommentErrorScript() string { return "" } func getPushToPullRequestBranchScript() string { return "" } -func getUpdateDiscussionScript() string { return "" } -func getUpdateIssueScript() string { return "" } func getUpdateProjectScript() string { return "" } func getUpdatePullRequestScript() string { return "" } func getUpdateReleaseScript() string { return "" } diff --git a/specs/safe-output-handler-manager.md b/specs/safe-output-handler-manager.md new file mode 100644 index 0000000000..36c20a484e --- /dev/null +++ b/specs/safe-output-handler-manager.md @@ -0,0 +1,269 @@ +# Safe Output Handler Manager Implementation + +## Overview + +This document describes the safe output handler manager implementation, which refactors the safe output processing system from multiple individual steps to a single dispatcher step. + +## Architecture + +### Current Multi-Step Approach + +Each safe output type (create_issue, add_comment, etc.) generates a separate step in the GitHub Actions workflow: + +```yaml +jobs: + safe_outputs: + steps: + - name: Setup Scripts + - name: Download agent output + - name: Create Issue + if: contains(fromJSON(env.GH_AW_AGENT_OUTPUT).items[*].type, 'create_issue') + uses: actions/github-script@... + with: + script: | + const { main } = require('./create_issue.cjs'); + await main(); + - name: Add Comment + if: contains(fromJSON(env.GH_AW_AGENT_OUTPUT).items[*].type, 'add_comment') + uses: actions/github-script@... + with: + script: | + const { main } = require('./add_comment.cjs'); + await main(); + # ... more steps for each safe output type +``` + +### New Handler Manager Approach + +A single step uses the handler manager to dispatch to appropriate handlers: + +```yaml +jobs: + safe_outputs: + steps: + - name: Setup Scripts + - name: Download agent output + - name: Process Safe Outputs + uses: actions/github-script@... + with: + script: | + const { main } = require('./safe_output_handler_manager.cjs'); + await main(); +``` + +## Implementation Details + +### Handler Manager (`safe_output_handler_manager.cjs`) + +The handler manager is responsible for: + +1. **Configuration Loading**: + ```javascript + function loadConfig() { + // Read from /tmp/gh-aw/safeoutputs/config.json + // Normalize keys (create-issue → create_issue) + return config; + } + ``` + +2. **Handler Registration**: + ```javascript + const HANDLER_MAP = { + create_issue: "./create_issue.cjs", + add_comment: "./add_comment.cjs", + create_discussion: "./create_discussion.cjs", + close_issue: "./close_issue.cjs", + close_discussion: "./close_discussion.cjs", + }; + + function loadHandlers(config) { + // Only load handlers for enabled types + // Store in Map + } + ``` + +3. **Message Processing**: + ```javascript + function processMessages(handlers, messages) { + // Process messages in order of appearance + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; + const handler = handlers.get(message.type); + + if (handler) { + await handler.main(); + } + } + } + ``` + +### Go Compiler Integration + +The Go compiler generates the handler manager step: + +```go +func (c *Compiler) buildHandlerManagerStep(data *WorkflowData) []string { + var steps []string + + steps = append(steps, " - name: Process Safe Outputs\n") + steps = append(steps, " id: process_safe_outputs\n") + steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script"))) + + // Add environment variables for all enabled handlers + c.addAllSafeOutputConfigEnvVars(&steps, data) + + // Call handler manager + steps = append(steps, " script: |\n") + steps = append(steps, " const { main } = require('"+SetupActionDestination+"/safe_output_handler_manager.cjs');\n") + steps = append(steps, " await main();\n") + + return steps +} +``` + +## Handler Compatibility + +All existing handlers are compatible without modification because they: + +1. **Export a `main()` function**: + ```javascript + async function main() { + // Handler logic + } + module.exports = { main }; + ``` + +2. **Access agent output internally**: + ```javascript + const result = loadAgentOutput(); + const items = result.items.filter(item => item.type === "create_issue"); + ``` + +3. **Read configuration from environment and config file**: + ```javascript + const titlePrefix = process.env.GH_AW_ISSUE_TITLE_PREFIX; + const config = getSafeOutputConfig("create_issue"); + ``` + +4. **Set outputs independently**: + ```javascript + core.setOutput("issue_number", issueNumber); + core.setOutput("issue_url", issueUrl); + ``` + +## Benefits + +### 1. Reduced Workflow Complexity +- **Before**: N steps (one per safe output type) +- **After**: 1 step (handler manager) +- Easier to read and understand workflow YAML + +### 2. Better Temporary ID Management +- Shared temporary ID map across all handlers +- Consistent ID resolution for cross-references +- Simpler debugging of ID-related issues + +### 3. Sequential Processing in Order of Appearance +- Messages are processed in the order they appear in the agent output file +- Preserves the natural flow and dependencies as written by the agent +- Simpler logic without complex ordering rules + +### 4. Easier Extensibility +- Add new handlers by updating `HANDLER_MAP` +- No Go code changes needed for new handler types +- Handlers remain isolated and testable + +### 5. Improved Error Handling +- Centralized error reporting +- Handlers can fail independently +- Better logging and diagnostics + +## Testing + +### Unit Tests (`safe_output_handler_manager.test.cjs`) + +Tests cover: +- Configuration loading and normalization +- Handler registration based on configuration +- Sequential message processing in order of appearance +- Error handling and recovery + +### Integration Testing + +To test the handler manager: + +1. Create a workflow with multiple safe output types +2. Compile the workflow +3. Verify single "Process Safe Outputs" step is generated +4. Run the workflow and verify correct behavior +5. Check that outputs match existing implementation + +## Migration Path + +### Phase 1: Foundation (Current) +- [x] Implement handler manager +- [x] Add tests +- [x] Add Go compiler functions + +### Phase 2: Integration (Next) +- [ ] Modify `buildConsolidatedSafeOutputsJob` to use handler manager +- [ ] Keep complex operations (PRs, assets) as separate steps +- [ ] Run integration tests + +### Phase 3: Validation +- [ ] Test with real workflows +- [ ] Compare outputs with existing implementation +- [ ] Performance testing + +### Phase 4: Cleanup +- [ ] Remove old multi-step approach +- [ ] Update documentation +- [ ] Release + +## Configuration Example + +```json +{ + "create-issue": { + "enabled": true, + "max": 5, + "title-prefix": "[AI]", + "labels": ["ai-generated"] + }, + "add-comment": { + "enabled": true, + "max": 3, + "target": "triggering" + }, + "create-discussion": { + "enabled": true + }, + "close-issue": { + "enabled": false + } +} +``` + +With this configuration, the handler manager will: +1. Load handlers for create_issue, add_comment, and create_discussion +2. Skip close_issue (disabled) +3. Process messages in the order they appear in the agent output file + +## Performance Considerations + +- **Handler Loading**: Handlers are loaded once at startup, not per message +- **Sequential Processing**: O(n) operation to process messages in order +- **Processing**: Each message is processed individually by its handler +- **Memory**: Temporary ID map is maintained in memory for the duration of the step + +## Future Enhancements + +1. **Batching**: Group consecutive messages of same type for batch processing +2. **Retry Logic**: Add retry for transient failures +3. **Metrics**: Collect processing metrics per handler +4. **Dynamic Loading**: Load handlers on-demand rather than upfront +5. **Handler Plugins**: Support external handler modules + +## Conclusion + +The handler manager implementation provides a cleaner, more maintainable architecture for safe output processing while maintaining full backward compatibility with existing handlers.