diff --git a/.changeset/patch-centralize-safe-output-validation.md b/.changeset/patch-centralize-safe-output-validation.md new file mode 100644 index 00000000000..2448051dd62 --- /dev/null +++ b/.changeset/patch-centralize-safe-output-validation.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Refactor safe output validation: centralize config defaults and eliminate duplicated validation code diff --git a/.github/workflows/ai-triage-campaign.lock.yml b/.github/workflows/ai-triage-campaign.lock.yml index de701a5fdf8..4c12ca04d87 100644 --- a/.github/workflows/ai-triage-campaign.lock.yml +++ b/.github/workflows/ai-triage-campaign.lock.yml @@ -508,7 +508,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"missing_tool":{},"noop":{"max":1},"update_project":{"max":20}} + {"missing_tool":{"max":0},"noop":{"max":1},"update_project":{"max":20}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index ed28b0bf177..4ebf989cf52 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1540,7 +1540,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index c5d7b2df0fb..3a360601add 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -442,7 +442,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 8ceaf1cbc1b..cb91ab53cc6 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1203,7 +1203,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index d995e02c2d2..0ed65664051 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -724,7 +724,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 0d5ff8523ef..8134c48dd64 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1413,7 +1413,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index b385d83775a..d82b83de3b2 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -1157,7 +1157,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{}} + {"missing_tool":{"max":0},"noop":{"max":1},"push_to_pull_request_branch":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Push changes to a pull request branch","inputSchema":{"additionalProperties":false,"properties":{"branch":{"description":"Optional branch name. Do not provide this parameter if you want to push changes from the current branch. If not provided, the current branch will be used.","type":"string"},"message":{"description":"Commit message","type":"string"},"pull_request_number":{"description":"Optional pull request number for target '*'","type":["number","string"]}},"required":["message"],"type":"object"},"name":"push_to_pull_request_branch"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 14bfdebf165..e0ac54d3b58 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -888,7 +888,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index ca397865422..6de0606b375 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -453,7 +453,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":5},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":5},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 3ffe28bb40c..895e545ad54 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -618,7 +618,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index cdefd1eefbc..92a62626613 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1772,7 +1772,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{}} + {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1},"push_to_pull_request_branch":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Push changes to a pull request branch","inputSchema":{"additionalProperties":false,"properties":{"branch":{"description":"Optional branch name. Do not provide this parameter if you want to push changes from the current branch. If not provided, the current branch will be used.","type":"string"},"message":{"description":"Commit message","type":"string"},"pull_request_number":{"description":"Optional pull request number for target '*'","type":["number","string"]}},"required":["message"],"type":"object"},"name":"push_to_pull_request_branch"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index a5a5ea51a34..1352b7febd1 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -695,7 +695,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 1cd59623d85..ee74f202267 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -1100,7 +1100,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index 5e82d188804..76a01d08695 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -1250,7 +1250,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index c219d7953b1..4302ddd07aa 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -823,7 +823,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index b6df98b5de9..759b32d3cd6 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -1933,7 +1933,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 57c3db7681d..bc4fa7e90d3 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -1566,7 +1566,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"push_to_pull_request_branch":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Push changes to a pull request branch","inputSchema":{"additionalProperties":false,"properties":{"branch":{"description":"Optional branch name. Do not provide this parameter if you want to push changes from the current branch. If not provided, the current branch will be used.","type":"string"},"message":{"description":"Commit message","type":"string"},"pull_request_number":{"description":"Optional pull request number for target '*'","type":["number","string"]}},"required":["message"],"type":"object"},"name":"push_to_pull_request_branch"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index bc798f7a932..70381db2b3f 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -1278,7 +1278,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index f067325f32d..a6b43efaa8e 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -565,7 +565,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index dce447889b7..58f8f8c5596 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -624,7 +624,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 5d8e53afec6..26dc85d7c8e 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -951,7 +951,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 067071a97ee..19fd4ee08c9 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -487,7 +487,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 90f45eda3d6..d903d2ed782 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -1126,7 +1126,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index 43c4bbdb4cf..3b6d66a3620 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -996,7 +996,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index 7c46061bf21..9f0cc0f40a1 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -387,7 +387,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] @@ -3118,6 +3118,49 @@ jobs: return `${summary}\n\n`; } } + function parseLogEntries(logContent) { + let logEntries; + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + return logEntries; + } catch (jsonArrayError) { + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; + } + if (trimmedLine.startsWith("[{")) { + try { + const arrayEntries = JSON.parse(trimmedLine); + if (Array.isArray(arrayEntries)) { + logEntries.push(...arrayEntries); + continue; + } + } catch (arrayParseError) { + continue; + } + } + if (!trimmedLine.startsWith("{")) { + continue; + } + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + continue; + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { + return null; + } + return logEntries; + } function main() { runLogParser({ parseLog: parseCopilotLog, @@ -3155,37 +3198,10 @@ jobs: if (debugLogEntries && debugLogEntries.length > 0) { logEntries = debugLogEntries; } else { - logEntries = []; - const lines = logContent.split("\n"); - for (const line of lines) { - const trimmedLine = line.trim(); - if (trimmedLine === "") { - continue; - } - if (trimmedLine.startsWith("[{")) { - try { - const arrayEntries = JSON.parse(trimmedLine); - if (Array.isArray(arrayEntries)) { - logEntries.push(...arrayEntries); - continue; - } - } catch (arrayParseError) { - continue; - } - } - if (!trimmedLine.startsWith("{")) { - continue; - } - try { - const jsonEntry = JSON.parse(trimmedLine); - logEntries.push(jsonEntry); - } catch (jsonLineError) { - continue; - } - } + logEntries = parseLogEntries(logContent); } } - if (!Array.isArray(logEntries) || logEntries.length === 0) { + if (!logEntries) { return "## Agent Log Summary\n\nLog format not recognized as Copilot JSON array or JSONL.\n"; } const conversationResult = generateConversationMarkdown(logEntries, { diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index 4f080046586..7ed74139c03 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -700,7 +700,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"close_issue":{"max":20,"required_title_prefix":"[deps]"},"create_issue":{"max":10},"missing_tool":{},"noop":{"max":1}} + {"close_issue":{"max":20,"required_title_prefix":"[deps]"},"create_issue":{"max":10},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Close a GitHub issue with a comment","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body to add when closing the issue","type":"string"},"issue_number":{"description":"Optional issue number (uses triggering issue if not provided)","type":["number","string"]}},"required":["body"],"type":"object"},"name":"close_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index f536a84a0f9..b8df5caa1ab 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -809,7 +809,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1,"target":"*"},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1,"target":"*"},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 2ebc546b4ea..e6a65fb7316 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -288,7 +288,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"close_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"close_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Close a GitHub discussion with a comment and optional resolution reason","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body to add when closing the discussion","type":"string"},"discussion_number":{"description":"Optional discussion number (uses triggering discussion if not provided)","type":["number","string"]},"reason":{"description":"Optional resolution reason","enum":["RESOLVED","DUPLICATE","OUTDATED","ANSWERED"],"type":"string"}},"required":["body"],"type":"object"},"name":"close_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 720ee955c84..8878ac482be 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -1097,7 +1097,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index ef79bc550fd..57d769597d4 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -424,7 +424,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index b6f717e9c8a..bb640586210 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -448,7 +448,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 625c9c7114d..0a5c1ec233d 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -531,7 +531,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":3},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":3},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index c52d2f1541e..25dbaeb0665 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -499,7 +499,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 6d7b20a017c..38d9f6d47d1 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -945,7 +945,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 9fb7e4f044c..4e6ba6088bc 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -948,7 +948,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 9974a273c2b..9d922a244da 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -681,7 +681,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 942bedb9a74..8d481c1579f 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -529,7 +529,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 1d71af9ecae..95eaa503770 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1461,7 +1461,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_pull_request_review_comment":{"max":5},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"create_pull_request_review_comment":{"max":5},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a review comment on a GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body content","type":"string"},"line":{"description":"Line number for the comment","type":["number","string"]},"path":{"description":"File path for the review comment","type":"string"},"side":{"description":"Optional side of the diff: LEFT or RIGHT","enum":["LEFT","RIGHT"],"type":"string"},"start_line":{"description":"Optional start line for multi-line comments","type":["number","string"]}},"required":["path","line","body"],"type":"object"},"name":"create_pull_request_review_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index d05e1c6fca9..698cd4f2541 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -564,7 +564,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index ccd4e18c0fb..61e89069e0a 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -893,20 +893,6 @@ jobs: with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - function sanitizeLabelContent(content) { - if (!content || typeof content !== "string") { - return ""; - } - let sanitized = content.trim(); - sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); - sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); - sanitized = sanitized.replace( - /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, - (_m, p1, p2) => `${p1}\`@${p2}\`` - ); - sanitized = sanitized.replace(/[<>&'"]/g, ""); - return sanitized.trim(); - } const fs = require("fs"); function loadAgentOutput() { const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT; @@ -1077,6 +1063,107 @@ jobs: contextType: contextType || (supportsPR ? "issue" : "pull request"), }; } + function sanitizeLabelContent(content) { + if (!content || typeof content !== "string") { + return ""; + } + let sanitized = content.trim(); + sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); + sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); + sanitized = sanitized.replace( + /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, + (_m, p1, p2) => `${p1}\`@${p2}\`` + ); + sanitized = sanitized.replace(/[<>&'"]/g, ""); + return sanitized.trim(); + } + function loadSafeOutputsConfig() { + const configPath = "/tmp/gh-aw/safeoutputs/config.json"; + try { + if (!fs.existsSync(configPath)) { + core.warning(`Config file not found at ${configPath}, using defaults`); + return {}; + } + const configContent = fs.readFileSync(configPath, "utf8"); + return JSON.parse(configContent); + } catch (error) { + core.warning(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`); + return {}; + } + } + function getSafeOutputConfig(outputType) { + const config = loadSafeOutputsConfig(); + return config[outputType] || {}; + } + function validateTitle(title, fieldName = "title") { + if (title === undefined || title === null) { + return { valid: false, error: `${fieldName} is required` }; + } + if (typeof title !== "string") { + return { valid: false, error: `${fieldName} must be a string` }; + } + const trimmed = title.trim(); + if (trimmed.length === 0) { + return { valid: false, error: `${fieldName} cannot be empty` }; + } + return { valid: true, value: trimmed }; + } + function validateBody(body, fieldName = "body", required = false) { + if (body === undefined || body === null) { + if (required) { + return { valid: false, error: `${fieldName} is required` }; + } + return { valid: true, value: "" }; + } + if (typeof body !== "string") { + return { valid: false, error: `${fieldName} must be a string` }; + } + return { valid: true, value: body }; + } + function validateLabels(labels, allowedLabels = undefined, maxCount = 3) { + if (!labels || !Array.isArray(labels)) { + return { valid: false, error: "labels must be an array" }; + } + for (const label of labels) { + if (label && typeof label === "string" && label.startsWith("-")) { + return { valid: false, error: `Label removal is not permitted. Found line starting with '-': ${label}` }; + } + } + let validLabels = labels; + if (allowedLabels && allowedLabels.length > 0) { + validLabels = labels.filter(label => allowedLabels.includes(label)); + } + const uniqueLabels = validLabels + .filter(label => label != null && label !== false && label !== 0) + .map(label => String(label).trim()) + .filter(label => label) + .map(label => sanitizeLabelContent(label)) + .filter(label => label) + .map(label => (label.length > 64 ? label.substring(0, 64) : label)) + .filter((label, index, arr) => arr.indexOf(label) === index); + if (uniqueLabels.length > maxCount) { + core.info(`Too many labels (${uniqueLabels.length}), limiting to ${maxCount}`); + return { valid: true, value: uniqueLabels.slice(0, maxCount) }; + } + if (uniqueLabels.length === 0) { + return { valid: false, error: "No valid labels found after sanitization" }; + } + return { valid: true, value: uniqueLabels }; + } + function validateMaxCount(envValue, configDefault, fallbackDefault = 1) { + const defaultValue = configDefault !== undefined ? configDefault : fallbackDefault; + if (!envValue) { + return { valid: true, value: defaultValue }; + } + const parsed = parseInt(envValue, 10); + if (isNaN(parsed) || parsed < 1) { + return { + valid: false, + error: `Invalid max value: ${envValue}. Must be a positive integer`, + }; + } + return { valid: true, value: parsed }; + } async function main() { const result = loadAgentOutput(); if (!result.success) { @@ -1108,13 +1195,14 @@ jobs: }); return; } - const allowedLabels = parseAllowedItems(process.env.GH_AW_LABELS_ALLOWED); + const config = getSafeOutputConfig("add_labels"); + const allowedLabels = parseAllowedItems(process.env.GH_AW_LABELS_ALLOWED) || config.allowed; if (allowedLabels) { core.info(`Allowed labels: ${JSON.stringify(allowedLabels)}`); } else { core.info("No label restrictions - any labels are allowed"); } - const maxCountResult = parseMaxCount(process.env.GH_AW_LABELS_MAX_COUNT, 3); + const maxCountResult = validateMaxCount(process.env.GH_AW_LABELS_MAX_COUNT, config.max); if (!maxCountResult.valid) { core.setFailed(maxCountResult.error); return; @@ -1142,30 +1230,25 @@ jobs: const contextType = targetResult.contextType; const requestedLabels = labelsItem.labels || []; core.info(`Requested labels: ${JSON.stringify(requestedLabels)}`); - for (const label of requestedLabels) { - if (label && typeof label === "string" && label.startsWith("-")) { - core.setFailed(`Label removal is not permitted. Found line starting with '-': ${label}`); + const labelsResult = validateLabels(requestedLabels, allowedLabels, maxCount); + if (!labelsResult.valid) { + if (labelsResult.error && labelsResult.error.includes("No valid labels")) { + core.info("No labels to add"); + core.setOutput("labels_added", ""); + await core.summary + .addRaw( + ` + ## Label Addition + No labels were added (no valid labels found in agent output). + ` + ) + .write(); return; } + core.setFailed(labelsResult.error || "Invalid labels"); + return; } - let validLabels; - if (allowedLabels) { - validLabels = requestedLabels.filter(label => allowedLabels.includes(label)); - } else { - validLabels = requestedLabels; - } - let uniqueLabels = validLabels - .filter(label => label != null && label !== false && label !== 0) - .map(label => String(label).trim()) - .filter(label => label) - .map(label => sanitizeLabelContent(label)) - .filter(label => label) - .map(label => (label.length > 64 ? label.substring(0, 64) : label)) - .filter((label, index, arr) => arr.indexOf(label) === index); - if (uniqueLabels.length > maxCount) { - core.info(`too many labels, keep ${maxCount}`); - uniqueLabels = uniqueLabels.slice(0, maxCount); - } + const uniqueLabels = labelsResult.value || []; if (uniqueLabels.length === 0) { core.info("No labels to add"); core.setOutput("labels_added", ""); @@ -1285,7 +1368,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_labels":{"allowed":["bug","feature","enhancement","documentation"],"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_labels":{"allowed":["bug","feature","enhancement","documentation"],"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add labels to a GitHub issue or pull request","inputSchema":{"additionalProperties":false,"properties":{"item_number":{"description":"Issue or PR number (optional for current context)","type":"number"},"labels":{"description":"Labels to add","items":{"type":"string"},"type":"array"}},"required":["labels"],"type":"object"},"name":"add_labels"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 407091f711e..ab57c99a7d7 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -817,7 +817,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 7828c3c4568..a39151a6551 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -954,7 +954,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"notion-add-comment":{"description":"Add a comment to a Notion page","inputs":{"comment":{"description":"The comment text to add","required":true,"type":"string"}},"output":"Comment added to Notion successfully!"},"post-to-slack-channel":{"description":"Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.","inputs":{"message":{"description":"The message to post (max 200 characters, supports Slack markdown)","required":true,"type":"string"}},"output":"Message posted to Slack successfully!"}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"notion-add-comment":{"description":"Add a comment to a Notion page","inputs":{"comment":{"description":"The comment text to add","required":true,"type":"string"}},"output":"Comment added to Notion successfully!"},"post-to-slack-channel":{"description":"Post a message to a Slack channel. Message must be 200 characters or less. Supports basic Slack markdown: *bold*, _italic_, ~strike~, `code`, ```code block```, \u003equote, and links \u003curl|text\u003e. Requires GH_AW_SLACK_CHANNEL_ID environment variable to be set.","inputs":{"message":{"description":"The message to post (max 200 characters, supports Slack markdown)","required":true,"type":"string"}},"output":"Message posted to Slack successfully!"}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 8c28a535cfb..5207eb0a5fd 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -911,7 +911,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{}} + {"missing_tool":{"max":0},"noop":{"max":1},"push_to_pull_request_branch":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Push changes to a pull request branch","inputSchema":{"additionalProperties":false,"properties":{"branch":{"description":"Optional branch name. Do not provide this parameter if you want to push changes from the current branch. If not provided, the current branch will be used.","type":"string"},"message":{"description":"Commit message","type":"string"},"pull_request_number":{"description":"Optional pull request number for target '*'","type":["number","string"]}},"required":["message"],"type":"object"},"name":"push_to_pull_request_branch"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index b7571405eba..f3117fe96bb 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1502,7 +1502,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 537f5dbb368..ab64020b666 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1023,7 +1023,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"close_discussion":{"max":1,"required_category":"Ideas"},"create_issue":{"max":5},"missing_tool":{},"noop":{"max":1}} + {"close_discussion":{"max":1,"required_category":"Ideas"},"create_issue":{"max":5},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Close a GitHub discussion with a comment and optional resolution reason","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body to add when closing the discussion","type":"string"},"discussion_number":{"description":"Optional discussion number (uses triggering discussion if not provided)","type":["number","string"]},"reason":{"description":"Optional resolution reason","enum":["RESOLVED","DUPLICATE","OUTDATED","ANSWERED"],"type":"string"}},"required":["body"],"type":"object"},"name":"close_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index fcb1bd778c3..421efbb85c7 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1335,20 +1335,6 @@ jobs: with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | - function sanitizeLabelContent(content) { - if (!content || typeof content !== "string") { - return ""; - } - let sanitized = content.trim(); - sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); - sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); - sanitized = sanitized.replace( - /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, - (_m, p1, p2) => `${p1}\`@${p2}\`` - ); - sanitized = sanitized.replace(/[<>&'"]/g, ""); - return sanitized.trim(); - } const fs = require("fs"); function loadAgentOutput() { const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT; @@ -1519,6 +1505,107 @@ jobs: contextType: contextType || (supportsPR ? "issue" : "pull request"), }; } + function sanitizeLabelContent(content) { + if (!content || typeof content !== "string") { + return ""; + } + let sanitized = content.trim(); + sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); + sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); + sanitized = sanitized.replace( + /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, + (_m, p1, p2) => `${p1}\`@${p2}\`` + ); + sanitized = sanitized.replace(/[<>&'"]/g, ""); + return sanitized.trim(); + } + function loadSafeOutputsConfig() { + const configPath = "/tmp/gh-aw/safeoutputs/config.json"; + try { + if (!fs.existsSync(configPath)) { + core.warning(`Config file not found at ${configPath}, using defaults`); + return {}; + } + const configContent = fs.readFileSync(configPath, "utf8"); + return JSON.parse(configContent); + } catch (error) { + core.warning(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`); + return {}; + } + } + function getSafeOutputConfig(outputType) { + const config = loadSafeOutputsConfig(); + return config[outputType] || {}; + } + function validateTitle(title, fieldName = "title") { + if (title === undefined || title === null) { + return { valid: false, error: `${fieldName} is required` }; + } + if (typeof title !== "string") { + return { valid: false, error: `${fieldName} must be a string` }; + } + const trimmed = title.trim(); + if (trimmed.length === 0) { + return { valid: false, error: `${fieldName} cannot be empty` }; + } + return { valid: true, value: trimmed }; + } + function validateBody(body, fieldName = "body", required = false) { + if (body === undefined || body === null) { + if (required) { + return { valid: false, error: `${fieldName} is required` }; + } + return { valid: true, value: "" }; + } + if (typeof body !== "string") { + return { valid: false, error: `${fieldName} must be a string` }; + } + return { valid: true, value: body }; + } + function validateLabels(labels, allowedLabels = undefined, maxCount = 3) { + if (!labels || !Array.isArray(labels)) { + return { valid: false, error: "labels must be an array" }; + } + for (const label of labels) { + if (label && typeof label === "string" && label.startsWith("-")) { + return { valid: false, error: `Label removal is not permitted. Found line starting with '-': ${label}` }; + } + } + let validLabels = labels; + if (allowedLabels && allowedLabels.length > 0) { + validLabels = labels.filter(label => allowedLabels.includes(label)); + } + const uniqueLabels = validLabels + .filter(label => label != null && label !== false && label !== 0) + .map(label => String(label).trim()) + .filter(label => label) + .map(label => sanitizeLabelContent(label)) + .filter(label => label) + .map(label => (label.length > 64 ? label.substring(0, 64) : label)) + .filter((label, index, arr) => arr.indexOf(label) === index); + if (uniqueLabels.length > maxCount) { + core.info(`Too many labels (${uniqueLabels.length}), limiting to ${maxCount}`); + return { valid: true, value: uniqueLabels.slice(0, maxCount) }; + } + if (uniqueLabels.length === 0) { + return { valid: false, error: "No valid labels found after sanitization" }; + } + return { valid: true, value: uniqueLabels }; + } + function validateMaxCount(envValue, configDefault, fallbackDefault = 1) { + const defaultValue = configDefault !== undefined ? configDefault : fallbackDefault; + if (!envValue) { + return { valid: true, value: defaultValue }; + } + const parsed = parseInt(envValue, 10); + if (isNaN(parsed) || parsed < 1) { + return { + valid: false, + error: `Invalid max value: ${envValue}. Must be a positive integer`, + }; + } + return { valid: true, value: parsed }; + } async function main() { const result = loadAgentOutput(); if (!result.success) { @@ -1550,13 +1637,14 @@ jobs: }); return; } - const allowedLabels = parseAllowedItems(process.env.GH_AW_LABELS_ALLOWED); + const config = getSafeOutputConfig("add_labels"); + const allowedLabels = parseAllowedItems(process.env.GH_AW_LABELS_ALLOWED) || config.allowed; if (allowedLabels) { core.info(`Allowed labels: ${JSON.stringify(allowedLabels)}`); } else { core.info("No label restrictions - any labels are allowed"); } - const maxCountResult = parseMaxCount(process.env.GH_AW_LABELS_MAX_COUNT, 3); + const maxCountResult = validateMaxCount(process.env.GH_AW_LABELS_MAX_COUNT, config.max); if (!maxCountResult.valid) { core.setFailed(maxCountResult.error); return; @@ -1584,30 +1672,25 @@ jobs: const contextType = targetResult.contextType; const requestedLabels = labelsItem.labels || []; core.info(`Requested labels: ${JSON.stringify(requestedLabels)}`); - for (const label of requestedLabels) { - if (label && typeof label === "string" && label.startsWith("-")) { - core.setFailed(`Label removal is not permitted. Found line starting with '-': ${label}`); + const labelsResult = validateLabels(requestedLabels, allowedLabels, maxCount); + if (!labelsResult.valid) { + if (labelsResult.error && labelsResult.error.includes("No valid labels")) { + core.info("No labels to add"); + core.setOutput("labels_added", ""); + await core.summary + .addRaw( + ` + ## Label Addition + No labels were added (no valid labels found in agent output). + ` + ) + .write(); return; } + core.setFailed(labelsResult.error || "Invalid labels"); + return; } - let validLabels; - if (allowedLabels) { - validLabels = requestedLabels.filter(label => allowedLabels.includes(label)); - } else { - validLabels = requestedLabels; - } - let uniqueLabels = validLabels - .filter(label => label != null && label !== false && label !== 0) - .map(label => String(label).trim()) - .filter(label => label) - .map(label => sanitizeLabelContent(label)) - .filter(label => label) - .map(label => (label.length > 64 ? label.substring(0, 64) : label)) - .filter((label, index, arr) => arr.indexOf(label) === index); - if (uniqueLabels.length > maxCount) { - core.info(`too many labels, keep ${maxCount}`); - uniqueLabels = uniqueLabels.slice(0, maxCount); - } + const uniqueLabels = labelsResult.value || []; if (uniqueLabels.length === 0) { core.info("No labels to add"); core.setOutput("labels_added", ""); @@ -1775,7 +1858,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":3,"target":"*"},"add_labels":{"allowed":["poetry","creative","automation","ai-generated","epic","haiku","sonnet","limerick"],"max":5},"create_issue":{"max":2},"create_pull_request":{},"create_pull_request_review_comment":{"max":2},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{},"update_issue":{"max":2},"upload_asset":{}} + {"add_comment":{"max":3,"target":"*"},"add_labels":{"allowed":["poetry","creative","automation","ai-generated","epic","haiku","sonnet","limerick"],"max":5},"create_issue":{"max":2},"create_pull_request":{},"create_pull_request_review_comment":{"max":2},"missing_tool":{"max":0},"noop":{"max":1},"push_to_pull_request_branch":{"max":0},"update_issue":{"max":2},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Create a review comment on a GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body content","type":"string"},"line":{"description":"Line number for the comment","type":["number","string"]},"path":{"description":"File path for the review comment","type":"string"},"side":{"description":"Optional side of the diff: LEFT or RIGHT","enum":["LEFT","RIGHT"],"type":"string"},"start_line":{"description":"Optional start line for multi-line comments","type":["number","string"]}},"required":["path","line","body"],"type":"object"},"name":"create_pull_request_review_comment"},{"description":"Add labels to a GitHub issue or pull request","inputSchema":{"additionalProperties":false,"properties":{"item_number":{"description":"Issue or PR number (optional for current context)","type":"number"},"labels":{"description":"Labels to add","items":{"type":"string"},"type":"array"}},"required":["labels"],"type":"object"},"name":"add_labels"},{"description":"Update a GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Optional new issue body","type":"string"},"issue_number":{"description":"Optional issue number for target '*'","type":["number","string"]},"status":{"description":"Optional new issue status","enum":["open","closed"],"type":"string"},"title":{"description":"Optional new issue title","type":"string"}},"type":"object"},"name":"update_issue"},{"description":"Push changes to a pull request branch","inputSchema":{"additionalProperties":false,"properties":{"branch":{"description":"Optional branch name. Do not provide this parameter if you want to push changes from the current branch. If not provided, the current branch will be used.","type":"string"},"message":{"description":"Commit message","type":"string"},"pull_request_number":{"description":"Optional pull request number for target '*'","type":["number","string"]}},"required":["message"],"type":"object"},"name":"push_to_pull_request_branch"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index fa03e98df1a..1016c40fa5f 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1519,7 +1519,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":3},"create_discussion":{"max":1},"create_pull_request_review_comment":{"max":10},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":3},"create_discussion":{"max":1},"create_pull_request_review_comment":{"max":10},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a review comment on a GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body content","type":"string"},"line":{"description":"Line number for the comment","type":["number","string"]},"path":{"description":"File path for the review comment","type":"string"},"side":{"description":"Optional side of the diff: LEFT or RIGHT","enum":["LEFT","RIGHT"],"type":"string"},"start_line":{"description":"Optional start line for multi-line comments","type":["number","string"]}},"required":["path","line","body"],"type":"object"},"name":"create_pull_request_review_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index d9700fc529e..ee4d6ebfbb4 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -1494,7 +1494,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 2831f6cac33..54132ef6df4 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -1315,7 +1315,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index fd5af096c97..6b36fdb4f94 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1779,7 +1779,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/release-highlights.lock.yml b/.github/workflows/release-highlights.lock.yml index 6d75a961a8a..f7859767657 100644 --- a/.github/workflows/release-highlights.lock.yml +++ b/.github/workflows/release-highlights.lock.yml @@ -508,7 +508,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"missing_tool":{},"noop":{"max":1},"update_release":{"max":1}} + {"missing_tool":{"max":0},"noop":{"max":1},"update_release":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Update a GitHub release description. IMPORTANT: You must provide the 'tag' parameter with the release tag name (e.g., 'v1.0.0'). The tag cannot be inferred from event context in all scenarios.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Release body content to set, append, or prepend","type":"string"},"operation":{"description":"Update operation: 'replace' (full replacement), 'append' (add at end with separator), or 'prepend' (add at start with separator)","enum":["replace","append","prepend"],"type":"string"},"tag":{"description":"Release tag name (REQUIRED - e.g., 'v1.0.0'). This must be provided explicitly.","type":"string"}},"required":["tag","operation","body"],"type":"object"},"name":"update_release"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 4bdbe35592c..9d503f05536 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -468,7 +468,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index a30f3b32e8f..b50bd6f7442 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -936,7 +936,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index d7fc00c2de9..b6d92ee4e1a 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -392,7 +392,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index 9edaf51aa1e..4b786a26750 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -934,7 +934,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index efd408efb93..4f21ed36caa 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -821,7 +821,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index b63cfb42dee..4904c4d2ade 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1853,7 +1853,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index bcadd3f95d1..65645a1422f 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -552,7 +552,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 30d4e6b8bf6..ec1a5ac09f5 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -932,7 +932,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"close_issue":{"max":10,"required_title_prefix":"[refactor] "},"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"close_issue":{"max":10,"required_title_prefix":"[refactor] "},"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Close a GitHub issue with a comment","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body to add when closing the issue","type":"string"},"issue_number":{"description":"Optional issue number (uses triggering issue if not provided)","type":["number","string"]}},"required":["body"],"type":"object"},"name":"close_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 32ff7c9cbaf..2ffc039dc26 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -936,7 +936,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 1b51d4aeaff..ca2011596e0 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -712,7 +712,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index e455ec25a02..fec95c6ef5d 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -720,7 +720,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index b9e6b7c45fc..ba3e6822899 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -1527,7 +1527,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1,"target":"*"},"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":1,"target":"*"},"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 16dc96bbde7..be847295636 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -845,7 +845,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 451543ab150..409d8d96504 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -506,7 +506,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 0aa76f78065..8cd80387b81 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1150,7 +1150,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/test-assign-milestone-allowed.lock.yml b/.github/workflows/test-assign-milestone-allowed.lock.yml index 1fe8851fa28..5723278a945 100644 --- a/.github/workflows/test-assign-milestone-allowed.lock.yml +++ b/.github/workflows/test-assign-milestone-allowed.lock.yml @@ -387,7 +387,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"assign_milestone":{"allowed":["v1.0","v2.0","Sprint 1"],"max":3},"missing_tool":{},"noop":{"max":1}} + {"assign_milestone":{"allowed":["v1.0","v2.0","Sprint 1"],"max":3},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Assign an issue to a milestone","inputSchema":{"additionalProperties":false,"properties":{"issue_number":{"description":"Issue number to assign milestone to","type":["number","string"]},"milestone_number":{"description":"Milestone number to assign","type":["number","string"]}},"required":["issue_number","milestone_number"],"type":"object"},"name":"assign_milestone"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/test-claude-assign-milestone.lock.yml b/.github/workflows/test-claude-assign-milestone.lock.yml index 3b6593b70d7..aba31d18f09 100644 --- a/.github/workflows/test-claude-assign-milestone.lock.yml +++ b/.github/workflows/test-claude-assign-milestone.lock.yml @@ -381,7 +381,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"assign_milestone":{"max":2},"missing_tool":{},"noop":{"max":1}} + {"assign_milestone":{"max":2},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Assign an issue to a milestone","inputSchema":{"additionalProperties":false,"properties":{"issue_number":{"description":"Issue number to assign milestone to","type":["number","string"]},"milestone_number":{"description":"Milestone number to assign","type":["number","string"]}},"required":["issue_number","milestone_number"],"type":"object"},"name":"assign_milestone"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/test-close-discussion.lock.yml b/.github/workflows/test-close-discussion.lock.yml index 5fb0f26aa38..2ce157b01b0 100644 --- a/.github/workflows/test-close-discussion.lock.yml +++ b/.github/workflows/test-close-discussion.lock.yml @@ -293,7 +293,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"close_discussion":{"max":1,"required_category":"Ideas"},"missing_tool":{},"noop":{"max":1}} + {"close_discussion":{"max":1,"required_category":"Ideas"},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Close a GitHub discussion with a comment and optional resolution reason","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body to add when closing the discussion","type":"string"},"discussion_number":{"description":"Optional discussion number (uses triggering discussion if not provided)","type":["number","string"]},"reason":{"description":"Optional resolution reason","enum":["RESOLVED","DUPLICATE","OUTDATED","ANSWERED"],"type":"string"}},"required":["body"],"type":"object"},"name":"close_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/test-codex-assign-milestone.lock.yml b/.github/workflows/test-codex-assign-milestone.lock.yml index 09519dd2e68..6482c5f911d 100644 --- a/.github/workflows/test-codex-assign-milestone.lock.yml +++ b/.github/workflows/test-codex-assign-milestone.lock.yml @@ -272,7 +272,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"assign_milestone":{"max":2},"missing_tool":{},"noop":{"max":1}} + {"assign_milestone":{"max":2},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Assign an issue to a milestone","inputSchema":{"additionalProperties":false,"properties":{"issue_number":{"description":"Issue number to assign milestone to","type":["number","string"]},"milestone_number":{"description":"Milestone number to assign","type":["number","string"]}},"required":["issue_number","milestone_number"],"type":"object"},"name":"assign_milestone"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/test-copilot-assign-milestone.lock.yml b/.github/workflows/test-copilot-assign-milestone.lock.yml index 67bf18ec1fb..b390febd6c1 100644 --- a/.github/workflows/test-copilot-assign-milestone.lock.yml +++ b/.github/workflows/test-copilot-assign-milestone.lock.yml @@ -272,7 +272,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"assign_milestone":{"max":2},"missing_tool":{},"noop":{"max":1}} + {"assign_milestone":{"max":2},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Assign an issue to a milestone","inputSchema":{"additionalProperties":false,"properties":{"issue_number":{"description":"Issue number to assign milestone to","type":["number","string"]},"milestone_number":{"description":"Milestone number to assign","type":["number","string"]}},"required":["issue_number","milestone_number"],"type":"object"},"name":"assign_milestone"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/test-ollama-threat-detection.lock.yml b/.github/workflows/test-ollama-threat-detection.lock.yml index 2543e6a2b86..1ea9dd350b9 100644 --- a/.github/workflows/test-ollama-threat-detection.lock.yml +++ b/.github/workflows/test-ollama-threat-detection.lock.yml @@ -278,7 +278,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index b83d82c27ca..3ffe6b649cd 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -722,7 +722,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_pull_request":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{}} + {"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1},"push_to_pull_request_branch":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Push changes to a pull request branch","inputSchema":{"additionalProperties":false,"properties":{"branch":{"description":"Optional branch name. Do not provide this parameter if you want to push changes from the current branch. If not provided, the current branch will be used.","type":"string"},"message":{"description":"Commit message","type":"string"},"pull_request_number":{"description":"Optional pull request number for target '*'","type":["number","string"]}},"required":["message"],"type":"object"},"name":"push_to_pull_request_branch"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 1d29c840177..d8ecd8eab3e 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -961,7 +961,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 297ced68e0c..2a10307da67 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1505,7 +1505,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"add_comment":{"max":1},"create_pull_request":{},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Add a comment to a GitHub issue, pull request, or discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Comment body/content","type":"string"},"item_number":{"description":"Issue, pull request or discussion number","type":"number"}},"required":["body","item_number"],"type":"object"},"name":"add_comment"},{"description":"Create a new GitHub pull request","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Pull request body/description","type":"string"},"branch":{"description":"Optional branch name. If not provided, the current branch will be used.","type":"string"},"labels":{"description":"Optional labels to add to the PR","items":{"type":"string"},"type":"array"},"title":{"description":"Pull request title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_pull_request"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index 308da1c5fb3..e0c5e3aea7c 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -552,7 +552,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_issue":{"max":1},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"max":1},"missing_tool":{"max":0},"noop":{"max":1}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub issue","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Issue body/description","type":"string"},"labels":{"description":"Issue labels","items":{"type":"string"},"type":"array"},"parent":{"description":"Parent issue number to create this issue as a sub-issue of","type":"number"},"title":{"description":"Issue title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_issue"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 50dcbfee8ee..6f35bcbf041 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -904,7 +904,7 @@ jobs: run: | mkdir -p /tmp/gh-aw/safeoutputs cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF' - {"create_discussion":{"max":1},"missing_tool":{},"noop":{"max":1},"upload_asset":{}} + {"create_discussion":{"max":1},"missing_tool":{"max":0},"noop":{"max":1},"upload_asset":{"max":0}} EOF cat > /tmp/gh-aw/safeoutputs/tools.json << 'EOF' [{"description":"Create a new GitHub discussion","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Discussion body/content","type":"string"},"category":{"description":"Discussion category","type":"string"},"title":{"description":"Discussion title","type":"string"}},"required":["title","body"],"type":"object"},"name":"create_discussion"},{"description":"Publish a file as a URL-addressable asset to an orphaned git branch","inputSchema":{"additionalProperties":false,"properties":{"path":{"description":"Path to the file to publish as an asset. Must be a file under the current workspace or /tmp directory. By default, images (.png, .jpg, .jpeg) are allowed, but can be configured via workflow settings.","type":"string"}},"required":["path"],"type":"object"},"name":"upload_asset"},{"description":"Report a missing tool or functionality needed to complete tasks","inputSchema":{"additionalProperties":false,"properties":{"alternatives":{"description":"Possible alternatives or workarounds (max 256 characters)","type":"string"},"reason":{"description":"Why this tool is needed (max 256 characters)","type":"string"},"tool":{"description":"Name of the missing tool (max 128 characters)","type":"string"}},"required":["tool","reason"],"type":"object"},"name":"missing_tool"},{"description":"Log a message for transparency when no significant actions are needed. Use this to ensure workflows produce human-visible artifacts even when no other actions are taken (e.g., 'Analysis complete - no issues found').","inputSchema":{"additionalProperties":false,"properties":{"message":{"description":"Message to log for transparency","type":"string"}},"required":["message"],"type":"object"},"name":"noop"}] diff --git a/pkg/workflow/js.go b/pkg/workflow/js.go index a06de479d49..9120909846b 100644 --- a/pkg/workflow/js.go +++ b/pkg/workflow/js.go @@ -101,6 +101,9 @@ var stagedPreviewScript string //go:embed js/safe_output_helpers.cjs var safeOutputHelpersScript string +//go:embed js/safe_output_validator.cjs +var safeOutputValidatorScript string + //go:embed js/is_truthy.cjs var isTruthyScript string @@ -159,6 +162,7 @@ func GetJavaScriptSources() map[string]string { "load_agent_output.cjs": loadAgentOutputScript, "staged_preview.cjs": stagedPreviewScript, "safe_output_helpers.cjs": safeOutputHelpersScript, + "safe_output_validator.cjs": safeOutputValidatorScript, "is_truthy.cjs": isTruthyScript, "log_parser_bootstrap.cjs": logParserBootstrapScript, "log_parser_shared.cjs": logParserSharedScript, diff --git a/pkg/workflow/js/add_labels.cjs b/pkg/workflow/js/add_labels.cjs index c5c29223c21..1f806a5d612 100644 --- a/pkg/workflow/js/add_labels.cjs +++ b/pkg/workflow/js/add_labels.cjs @@ -1,10 +1,10 @@ // @ts-check /// -const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); -const { parseAllowedItems, parseMaxCount, resolveTarget } = require("./safe_output_helpers.cjs"); +const { parseAllowedItems, resolveTarget } = require("./safe_output_helpers.cjs"); +const { getSafeOutputConfig, validateLabels, validateMaxCount } = require("./safe_output_validator.cjs"); async function main() { const result = loadAgentOutput(); @@ -39,16 +39,19 @@ async function main() { return; } - // Parse allowed labels - const allowedLabels = parseAllowedItems(process.env.GH_AW_LABELS_ALLOWED); + // Get configuration from config.json + const config = getSafeOutputConfig("add_labels"); + + // Parse allowed labels (from env or config) + const allowedLabels = parseAllowedItems(process.env.GH_AW_LABELS_ALLOWED) || config.allowed; if (allowedLabels) { core.info(`Allowed labels: ${JSON.stringify(allowedLabels)}`); } else { core.info("No label restrictions - any labels are allowed"); } - // Parse max count - const maxCountResult = parseMaxCount(process.env.GH_AW_LABELS_MAX_COUNT, 3); + // Parse max count (env takes priority, then config) + const maxCountResult = validateMaxCount(process.env.GH_AW_LABELS_MAX_COUNT, config.max); if (!maxCountResult.valid) { core.setFailed(maxCountResult.error); return; @@ -81,30 +84,32 @@ async function main() { const contextType = targetResult.contextType; const requestedLabels = labelsItem.labels || []; core.info(`Requested labels: ${JSON.stringify(requestedLabels)}`); - for (const label of requestedLabels) { - if (label && typeof label === "string" && label.startsWith("-")) { - core.setFailed(`Label removal is not permitted. Found line starting with '-': ${label}`); + + // Use validation helper to sanitize and validate labels + const labelsResult = validateLabels(requestedLabels, allowedLabels, maxCount); + if (!labelsResult.valid) { + // If no valid labels, log info and return gracefully instead of failing + if (labelsResult.error && labelsResult.error.includes("No valid labels")) { + core.info("No labels to add"); + core.setOutput("labels_added", ""); + await core.summary + .addRaw( + ` +## Label Addition + +No labels were added (no valid labels found in agent output). +` + ) + .write(); return; } + // For other validation errors, fail the workflow + core.setFailed(labelsResult.error || "Invalid labels"); + return; } - let validLabels; - if (allowedLabels) { - validLabels = requestedLabels.filter(label => allowedLabels.includes(label)); - } else { - validLabels = requestedLabels; - } - let uniqueLabels = validLabels - .filter(label => label != null && label !== false && label !== 0) - .map(label => String(label).trim()) - .filter(label => label) - .map(label => sanitizeLabelContent(label)) - .filter(label => label) - .map(label => (label.length > 64 ? label.substring(0, 64) : label)) - .filter((label, index, arr) => arr.indexOf(label) === index); - if (uniqueLabels.length > maxCount) { - core.info(`too many labels, keep ${maxCount}`); - uniqueLabels = uniqueLabels.slice(0, maxCount); - } + + const uniqueLabels = labelsResult.value || []; + if (uniqueLabels.length === 0) { core.info("No labels to add"); core.setOutput("labels_added", ""); diff --git a/pkg/workflow/js/add_labels.test.cjs b/pkg/workflow/js/add_labels.test.cjs index 6c7fc7c1ac2..df566d2eaac 100644 --- a/pkg/workflow/js/add_labels.test.cjs +++ b/pkg/workflow/js/add_labels.test.cjs @@ -144,6 +144,7 @@ describe("add_labels.cjs", () => { ], }); delete process.env.GH_AW_LABELS_ALLOWED; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label processing mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -169,6 +170,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = " "; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label processing mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -194,6 +196,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = "bug,enhancement"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label filtering mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -262,12 +265,12 @@ describe("add_labels.cjs", () => { // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); - expect(mockCore.info).toHaveBeenCalledWith("Max count: 3"); + expect(mockCore.info).toHaveBeenCalledWith("Max count: 1"); expect(mockGithub.rest.issues.addLabels).toHaveBeenCalledWith({ owner: "testowner", repo: "testrepo", issue_number: 123, - labels: ["bug", "enhancement", "feature"], // Only first 3 due to default max count + labels: ["bug"], // Only first 1 due to default max count }); }); }); @@ -415,6 +418,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = "bug,enhancement,feature"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label filtering // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); @@ -441,6 +445,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = "bug,enhancement"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label processing // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); @@ -481,6 +486,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = "bug,enhancement"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test deduplication // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); @@ -508,7 +514,7 @@ describe("add_labels.cjs", () => { // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); - expect(mockCore.info).toHaveBeenCalledWith("too many labels, keep 2"); + expect(mockCore.info).toHaveBeenCalledWith("Too many labels (5), limiting to 2"); expect(mockGithub.rest.issues.addLabels).toHaveBeenCalledWith({ owner: "testowner", repo: "testrepo", @@ -549,6 +555,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = "bug,enhancement,feature"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label addition mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -709,6 +716,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = "bug,enhancement"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test logging // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); @@ -728,6 +736,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_ALLOWED = " bug , enhancement , feature "; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label processing // Execute the script await eval(`(async () => { ${addLabelsScript} })()`); @@ -793,6 +802,7 @@ describe("add_labels.cjs", () => { }, ], }); + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test deduplication mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -842,6 +852,7 @@ describe("add_labels.cjs", () => { }, ], }); + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test length truncation mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -863,6 +874,7 @@ describe("add_labels.cjs", () => { }, ], }); + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test label filtering mockGithub.rest.issues.addLabels.mockResolvedValue({}); @@ -974,6 +986,7 @@ describe("add_labels.cjs", () => { ], }); process.env.GH_AW_LABELS_TARGET = "999"; + process.env.GH_AW_LABELS_MAX_COUNT = "10"; // Set high max to test target configuration // Context doesn't matter when explicit issue number is provided global.context.eventName = "push"; diff --git a/pkg/workflow/js/add_reviewer.cjs b/pkg/workflow/js/add_reviewer.cjs index bdf59ef0a37..723d563d06e 100644 --- a/pkg/workflow/js/add_reviewer.cjs +++ b/pkg/workflow/js/add_reviewer.cjs @@ -3,7 +3,8 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); -const { parseAllowedItems, parseMaxCount, resolveTarget } = require("./safe_output_helpers.cjs"); +const { parseAllowedItems, resolveTarget } = require("./safe_output_helpers.cjs"); +const { getSafeOutputConfig, validateMaxCount } = require("./safe_output_validator.cjs"); // GitHub Copilot reviewer bot username const COPILOT_REVIEWER_BOT = "copilot-pull-request-reviewer[bot]"; @@ -42,16 +43,19 @@ async function main() { return; } - // Parse allowed reviewers - const allowedReviewers = parseAllowedItems(process.env.GH_AW_REVIEWERS_ALLOWED); + // Get configuration from config.json + const config = getSafeOutputConfig("add_reviewer"); + + // Parse allowed reviewers (from env or config) + const allowedReviewers = parseAllowedItems(process.env.GH_AW_REVIEWERS_ALLOWED) || config.reviewers; if (allowedReviewers) { core.info(`Allowed reviewers: ${JSON.stringify(allowedReviewers)}`); } else { core.info("No reviewer restrictions - any reviewers are allowed"); } - // Parse max count - const maxCountResult = parseMaxCount(process.env.GH_AW_REVIEWERS_MAX_COUNT, 3); + // Parse max count (env takes priority, then config) + const maxCountResult = validateMaxCount(process.env.GH_AW_REVIEWERS_MAX_COUNT, config.max); if (!maxCountResult.valid) { core.setFailed(maxCountResult.error); return; diff --git a/pkg/workflow/js/add_reviewer.test.cjs b/pkg/workflow/js/add_reviewer.test.cjs index 0471defb8da..d2ecfcb8a8a 100644 --- a/pkg/workflow/js/add_reviewer.test.cjs +++ b/pkg/workflow/js/add_reviewer.test.cjs @@ -279,6 +279,7 @@ describe("add_reviewer", () => { }; fs.writeFileSync(outputFile, JSON.stringify(agentOutput)); process.env.GH_AW_AGENT_OUTPUT = outputFile; + process.env.GH_AW_REVIEWERS_MAX_COUNT = "10"; // Set high max to test deduplication await import("./add_reviewer.cjs"); diff --git a/pkg/workflow/js/safe_output_validator.cjs b/pkg/workflow/js/safe_output_validator.cjs new file mode 100644 index 00000000000..9ada5a53346 --- /dev/null +++ b/pkg/workflow/js/safe_output_validator.cjs @@ -0,0 +1,164 @@ +// @ts-check +/// + +const fs = require("fs"); +const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); + +/** + * Load and parse the safe outputs configuration from config.json + * @returns {object} The parsed configuration object + */ +function loadSafeOutputsConfig() { + const configPath = "/tmp/gh-aw/safeoutputs/config.json"; + try { + if (!fs.existsSync(configPath)) { + core.warning(`Config file not found at ${configPath}, using defaults`); + return {}; + } + const configContent = fs.readFileSync(configPath, "utf8"); + return JSON.parse(configContent); + } catch (error) { + core.warning(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`); + return {}; + } +} + +/** + * Get configuration for a specific safe output type + * @param {string} outputType - The type of safe output (e.g., "add_labels", "update_issue") + * @returns {{max?: number, target?: string, allowed?: string[]}} The configuration for this output type + */ +function getSafeOutputConfig(outputType) { + const config = loadSafeOutputsConfig(); + return config[outputType] || {}; +} + +/** + * Validate and sanitize a title string + * @param {any} title - The title to validate + * @param {string} fieldName - The name of the field for error messages (default: "title") + * @returns {{valid: boolean, value?: string, error?: string}} Validation result + */ +function validateTitle(title, fieldName = "title") { + if (title === undefined || title === null) { + return { valid: false, error: `${fieldName} is required` }; + } + + if (typeof title !== "string") { + return { valid: false, error: `${fieldName} must be a string` }; + } + + const trimmed = title.trim(); + if (trimmed.length === 0) { + return { valid: false, error: `${fieldName} cannot be empty` }; + } + + return { valid: true, value: trimmed }; +} + +/** + * Validate and sanitize a body/content string + * @param {any} body - The body to validate + * @param {string} fieldName - The name of the field for error messages (default: "body") + * @param {boolean} required - Whether the body is required (default: false) + * @returns {{valid: boolean, value?: string, error?: string}} Validation result + */ +function validateBody(body, fieldName = "body", required = false) { + if (body === undefined || body === null) { + if (required) { + return { valid: false, error: `${fieldName} is required` }; + } + return { valid: true, value: "" }; + } + + if (typeof body !== "string") { + return { valid: false, error: `${fieldName} must be a string` }; + } + + return { valid: true, value: body }; +} + +/** + * Validate and sanitize an array of labels + * @param {any} labels - The labels to validate + * @param {string[]|undefined} allowedLabels - Optional list of allowed labels + * @param {number} maxCount - Maximum number of labels allowed + * @returns {{valid: boolean, value?: string[], error?: string}} Validation result + */ +function validateLabels(labels, allowedLabels = undefined, maxCount = 3) { + if (!labels || !Array.isArray(labels)) { + return { valid: false, error: "labels must be an array" }; + } + + // Check for removal attempts (labels starting with '-') + for (const label of labels) { + if (label && typeof label === "string" && label.startsWith("-")) { + return { valid: false, error: `Label removal is not permitted. Found line starting with '-': ${label}` }; + } + } + + // Filter labels based on allowed list if provided + let validLabels = labels; + if (allowedLabels && allowedLabels.length > 0) { + validLabels = labels.filter(label => allowedLabels.includes(label)); + } + + // Sanitize and deduplicate labels + const uniqueLabels = validLabels + .filter(label => label != null && label !== false && label !== 0) + .map(label => String(label).trim()) + .filter(label => label) + .map(label => sanitizeLabelContent(label)) + .filter(label => label) + .map(label => (label.length > 64 ? label.substring(0, 64) : label)) + .filter((label, index, arr) => arr.indexOf(label) === index); + + // Apply max count limit + if (uniqueLabels.length > maxCount) { + core.info(`Too many labels (${uniqueLabels.length}), limiting to ${maxCount}`); + return { valid: true, value: uniqueLabels.slice(0, maxCount) }; + } + + if (uniqueLabels.length === 0) { + return { valid: false, error: "No valid labels found after sanitization" }; + } + + return { valid: true, value: uniqueLabels }; +} + +/** + * Validate max count from environment variable with config fallback + * @param {string|undefined} envValue - Environment variable value + * @param {number|undefined} configDefault - Default from config.json + * @param {number} [fallbackDefault] - Fallback default for testing (optional, defaults to 1) + * @returns {{valid: true, value: number} | {valid: false, error: string}} Validation result + */ +function validateMaxCount(envValue, configDefault, fallbackDefault = 1) { + // Priority: env var > config.json > fallback default + // In production, config.json should always have the default + // Fallback is provided for backward compatibility and testing + const defaultValue = configDefault !== undefined ? configDefault : fallbackDefault; + + if (!envValue) { + return { valid: true, value: defaultValue }; + } + + const parsed = parseInt(envValue, 10); + if (isNaN(parsed) || parsed < 1) { + return { + valid: false, + error: `Invalid max value: ${envValue}. Must be a positive integer`, + }; + } + + return { valid: true, value: parsed }; +} + +module.exports = { + loadSafeOutputsConfig, + getSafeOutputConfig, + validateTitle, + validateBody, + validateLabels, + validateMaxCount, +}; diff --git a/pkg/workflow/js/safe_output_validator.test.cjs b/pkg/workflow/js/safe_output_validator.test.cjs new file mode 100644 index 00000000000..e67e3502294 --- /dev/null +++ b/pkg/workflow/js/safe_output_validator.test.cjs @@ -0,0 +1,283 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; + +// Create mock functions +const mockExistsSync = vi.fn(() => false); +const mockReadFileSync = vi.fn(() => ""); + +// Mock fs module with proper CommonJS structure +vi.mock("fs", () => { + return { + existsSync: mockExistsSync, + readFileSync: mockReadFileSync, + default: { + existsSync: mockExistsSync, + readFileSync: mockReadFileSync, + }, + }; +}); + +// Mock the global core object +const mockCore = { + info: vi.fn(), + warning: vi.fn(), + error: vi.fn(), + setFailed: vi.fn(), +}; + +// Set up global +global.core = mockCore; + +describe("safe_output_validator.cjs", () => { + let validator; + + beforeEach(async () => { + vi.clearAllMocks(); + + // Reset mock implementations to default + mockExistsSync.mockReturnValue(false); + mockReadFileSync.mockReturnValue(""); + + // Dynamically import the module + validator = await import("./safe_output_validator.cjs"); + }); + + // Note: These tests are skipped because mocking fs.readFileSync for CJS modules + // is difficult in vitest. The validation functions below are what matters most. + describe.skip("loadSafeOutputsConfig", () => { + it("should load and parse config file", () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(JSON.stringify({ add_labels: { max: 5 } })); + + const config = validator.loadSafeOutputsConfig(); + + expect(config).toEqual({ add_labels: { max: 5 } }); + expect(mockReadFileSync).toHaveBeenCalledWith("/tmp/gh-aw/safeoutputs/config.json", "utf8"); + }); + + it("should return empty object if config file does not exist", () => { + mockExistsSync.mockReturnValue(false); + + const config = validator.loadSafeOutputsConfig(); + + expect(config).toEqual({}); + expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Config file not found")); + }); + + it("should return empty object on parse error", () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue("invalid json"); + + const config = validator.loadSafeOutputsConfig(); + + expect(config).toEqual({}); + expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to load config")); + }); + }); + + describe.skip("getSafeOutputConfig", () => { + it("should return config for specific output type", () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue( + JSON.stringify({ + add_labels: { max: 5, allowed: ["bug", "enhancement"] }, + update_issue: { max: 1 }, + }) + ); + + const config = validator.getSafeOutputConfig("add_labels"); + + expect(config).toEqual({ max: 5, allowed: ["bug", "enhancement"] }); + }); + + it("should return empty object for unknown output type", () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(JSON.stringify({ add_labels: { max: 5 } })); + + const config = validator.getSafeOutputConfig("unknown_type"); + + expect(config).toEqual({}); + }); + }); + + describe("validateTitle", () => { + it("should validate valid title", () => { + const result = validator.validateTitle(" Valid Title "); + + expect(result.valid).toBe(true); + expect(result.value).toBe("Valid Title"); + }); + + it("should reject undefined title", () => { + const result = validator.validateTitle(undefined); + + expect(result.valid).toBe(false); + expect(result.error).toContain("required"); + }); + + it("should reject non-string title", () => { + const result = validator.validateTitle(123); + + expect(result.valid).toBe(false); + expect(result.error).toContain("must be a string"); + }); + + it("should reject empty title", () => { + const result = validator.validateTitle(" "); + + expect(result.valid).toBe(false); + expect(result.error).toContain("cannot be empty"); + }); + + it("should use custom field name in error messages", () => { + const result = validator.validateTitle(undefined, "name"); + + expect(result.error).toContain("name is required"); + }); + }); + + describe("validateBody", () => { + it("should validate valid body", () => { + const result = validator.validateBody("Some content"); + + expect(result.valid).toBe(true); + expect(result.value).toBe("Some content"); + }); + + it("should allow undefined body when not required", () => { + const result = validator.validateBody(undefined, "body", false); + + expect(result.valid).toBe(true); + expect(result.value).toBe(""); + }); + + it("should reject undefined body when required", () => { + const result = validator.validateBody(undefined, "body", true); + + expect(result.valid).toBe(false); + expect(result.error).toContain("required"); + }); + + it("should reject non-string body", () => { + const result = validator.validateBody(123); + + expect(result.valid).toBe(false); + expect(result.error).toContain("must be a string"); + }); + }); + + describe("validateLabels", () => { + it("should validate and sanitize valid labels", () => { + const result = validator.validateLabels(["bug", " enhancement ", "documentation"], undefined, 10); + + expect(result.valid).toBe(true); + expect(result.value).toContain("bug"); + expect(result.value).toContain("enhancement"); + expect(result.value).toContain("documentation"); + }); + + it("should reject labels array with removal attempts", () => { + const result = validator.validateLabels(["-bug", "enhancement"], undefined, 10); + + expect(result.valid).toBe(false); + expect(result.error).toContain("Label removal is not permitted"); + }); + + it("should filter labels based on allowed list", () => { + const result = validator.validateLabels(["bug", "custom", "enhancement"], ["bug", "enhancement"], 10); + + expect(result.valid).toBe(true); + expect(result.value).toHaveLength(2); + expect(result.value).toContain("bug"); + expect(result.value).toContain("enhancement"); + expect(result.value).not.toContain("custom"); + }); + + it("should limit labels to max count", () => { + const result = validator.validateLabels(["a", "b", "c", "d", "e"], undefined, 3); + + expect(result.valid).toBe(true); + expect(result.value).toHaveLength(3); + expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("limiting to 3")); + }); + + it("should deduplicate labels", () => { + const result = validator.validateLabels(["bug", "bug", "enhancement"], undefined, 10); + + expect(result.valid).toBe(true); + expect(result.value).toHaveLength(2); + }); + + it("should truncate labels longer than 64 characters", () => { + const longLabel = "a".repeat(100); + const result = validator.validateLabels([longLabel], undefined, 10); + + expect(result.valid).toBe(true); + expect(result.value[0]).toHaveLength(64); + }); + + it("should reject non-array labels", () => { + const result = validator.validateLabels("bug", undefined, 10); + + expect(result.valid).toBe(false); + expect(result.error).toContain("must be an array"); + }); + + it("should reject when no valid labels remain", () => { + const result = validator.validateLabels([null, undefined, false, 0], undefined, 10); + + expect(result.valid).toBe(false); + expect(result.error).toContain("No valid labels found"); + }); + }); + + describe("validateMaxCount", () => { + it("should use fallback default when no config or env", () => { + const result = validator.validateMaxCount(undefined, undefined); + + expect(result.valid).toBe(true); + expect(result.value).toBe(1); // fallback default + }); + + it("should use config default when provided", () => { + const result = validator.validateMaxCount(undefined, 5); + + expect(result.valid).toBe(true); + expect(result.value).toBe(5); + }); + + it("should prefer env value over config", () => { + const result = validator.validateMaxCount("7", 5); + + expect(result.valid).toBe(true); + expect(result.value).toBe(7); + }); + + it("should use custom fallback when provided", () => { + const result = validator.validateMaxCount(undefined, undefined, 10); + + expect(result.valid).toBe(true); + expect(result.value).toBe(10); + }); + + it("should reject invalid env value", () => { + const result = validator.validateMaxCount("invalid", 5); + + expect(result.valid).toBe(false); + expect(result.error).toContain("Invalid max value"); + }); + + it("should reject negative env value", () => { + const result = validator.validateMaxCount("-1", 5); + + expect(result.valid).toBe(false); + expect(result.error).toContain("Invalid max value"); + }); + + it("should reject zero env value", () => { + const result = validator.validateMaxCount("0", 5); + + expect(result.valid).toBe(false); + expect(result.error).toContain("Invalid max value"); + }); + }); +}); diff --git a/pkg/workflow/safe_outputs.go b/pkg/workflow/safe_outputs.go index 52d679f0cf2..014e0ac7f81 100644 --- a/pkg/workflow/safe_outputs.go +++ b/pkg/workflow/safe_outputs.go @@ -920,16 +920,22 @@ func generateSafeOutputsConfig(data *WorkflowData) string { if data.SafeOutputs != nil { if data.SafeOutputs.CreateIssues != nil { issueConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.CreateIssues.Max > 0 { - issueConfig["max"] = data.SafeOutputs.CreateIssues.Max + maxValue = data.SafeOutputs.CreateIssues.Max } + issueConfig["max"] = maxValue safeOutputsConfig["create_issue"] = issueConfig } if data.SafeOutputs.CreateAgentTasks != nil { agentTaskConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.CreateAgentTasks.Max > 0 { - agentTaskConfig["max"] = data.SafeOutputs.CreateAgentTasks.Max + maxValue = data.SafeOutputs.CreateAgentTasks.Max } + agentTaskConfig["max"] = maxValue safeOutputsConfig["create_agent_task"] = agentTaskConfig } if data.SafeOutputs.AddComments != nil { @@ -937,23 +943,32 @@ func generateSafeOutputsConfig(data *WorkflowData) string { if data.SafeOutputs.AddComments.Target != "" { commentConfig["target"] = data.SafeOutputs.AddComments.Target } + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.AddComments.Max > 0 { - commentConfig["max"] = data.SafeOutputs.AddComments.Max + maxValue = data.SafeOutputs.AddComments.Max } + commentConfig["max"] = maxValue safeOutputsConfig["add_comment"] = commentConfig } if data.SafeOutputs.CreateDiscussions != nil { discussionConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.CreateDiscussions.Max > 0 { - discussionConfig["max"] = data.SafeOutputs.CreateDiscussions.Max + maxValue = data.SafeOutputs.CreateDiscussions.Max } + discussionConfig["max"] = maxValue safeOutputsConfig["create_discussion"] = discussionConfig } if data.SafeOutputs.CloseDiscussions != nil { closeDiscussionConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.CloseDiscussions.Max > 0 { - closeDiscussionConfig["max"] = data.SafeOutputs.CloseDiscussions.Max + maxValue = data.SafeOutputs.CloseDiscussions.Max } + closeDiscussionConfig["max"] = maxValue if data.SafeOutputs.CloseDiscussions.RequiredCategory != "" { closeDiscussionConfig["required_category"] = data.SafeOutputs.CloseDiscussions.RequiredCategory } @@ -967,9 +982,12 @@ func generateSafeOutputsConfig(data *WorkflowData) string { } if data.SafeOutputs.CloseIssues != nil { closeIssueConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.CloseIssues.Max > 0 { - closeIssueConfig["max"] = data.SafeOutputs.CloseIssues.Max + maxValue = data.SafeOutputs.CloseIssues.Max } + closeIssueConfig["max"] = maxValue if len(data.SafeOutputs.CloseIssues.RequiredLabels) > 0 { closeIssueConfig["required_labels"] = data.SafeOutputs.CloseIssues.RequiredLabels } @@ -985,34 +1003,59 @@ func generateSafeOutputsConfig(data *WorkflowData) string { } if data.SafeOutputs.CreatePullRequestReviewComments != nil { prReviewCommentConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 10 // default if data.SafeOutputs.CreatePullRequestReviewComments.Max > 0 { - prReviewCommentConfig["max"] = data.SafeOutputs.CreatePullRequestReviewComments.Max + maxValue = data.SafeOutputs.CreatePullRequestReviewComments.Max } + prReviewCommentConfig["max"] = maxValue safeOutputsConfig["create_pull_request_review_comment"] = prReviewCommentConfig } if data.SafeOutputs.CreateCodeScanningAlerts != nil { // Security reports typically have unlimited max, but check if configured securityReportConfig := map[string]any{} + // Always include max (use configured value or default of 0 for unlimited) + maxValue := 0 // default: unlimited if data.SafeOutputs.CreateCodeScanningAlerts.Max > 0 { - securityReportConfig["max"] = data.SafeOutputs.CreateCodeScanningAlerts.Max + maxValue = data.SafeOutputs.CreateCodeScanningAlerts.Max } + securityReportConfig["max"] = maxValue safeOutputsConfig["create_code_scanning_alert"] = securityReportConfig } if data.SafeOutputs.AddLabels != nil { labelConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 3 // default if data.SafeOutputs.AddLabels.Max > 0 { - labelConfig["max"] = data.SafeOutputs.AddLabels.Max + maxValue = data.SafeOutputs.AddLabels.Max } + labelConfig["max"] = maxValue if len(data.SafeOutputs.AddLabels.Allowed) > 0 { labelConfig["allowed"] = data.SafeOutputs.AddLabels.Allowed } safeOutputsConfig["add_labels"] = labelConfig } + if data.SafeOutputs.AddReviewer != nil { + reviewerConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 3 // default + if data.SafeOutputs.AddReviewer.Max > 0 { + maxValue = data.SafeOutputs.AddReviewer.Max + } + reviewerConfig["max"] = maxValue + if len(data.SafeOutputs.AddReviewer.Reviewers) > 0 { + reviewerConfig["reviewers"] = data.SafeOutputs.AddReviewer.Reviewers + } + safeOutputsConfig["add_reviewer"] = reviewerConfig + } if data.SafeOutputs.AssignMilestone != nil { assignMilestoneConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.AssignMilestone.Max > 0 { - assignMilestoneConfig["max"] = data.SafeOutputs.AssignMilestone.Max + maxValue = data.SafeOutputs.AssignMilestone.Max } + assignMilestoneConfig["max"] = maxValue if len(data.SafeOutputs.AssignMilestone.Allowed) > 0 { assignMilestoneConfig["allowed"] = data.SafeOutputs.AssignMilestone.Allowed } @@ -1020,9 +1063,12 @@ func generateSafeOutputsConfig(data *WorkflowData) string { } if data.SafeOutputs.UpdateIssues != nil { updateConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.UpdateIssues.Max > 0 { - updateConfig["max"] = data.SafeOutputs.UpdateIssues.Max + maxValue = data.SafeOutputs.UpdateIssues.Max } + updateConfig["max"] = maxValue safeOutputsConfig["update_issue"] = updateConfig } if data.SafeOutputs.PushToPullRequestBranch != nil { @@ -1030,44 +1076,62 @@ func generateSafeOutputsConfig(data *WorkflowData) string { if data.SafeOutputs.PushToPullRequestBranch.Target != "" { pushToBranchConfig["target"] = data.SafeOutputs.PushToPullRequestBranch.Target } + // Always include max (use configured value or default of 0 for unlimited) + maxValue := 0 // default: unlimited if data.SafeOutputs.PushToPullRequestBranch.Max > 0 { - pushToBranchConfig["max"] = data.SafeOutputs.PushToPullRequestBranch.Max + maxValue = data.SafeOutputs.PushToPullRequestBranch.Max } + pushToBranchConfig["max"] = maxValue safeOutputsConfig["push_to_pull_request_branch"] = pushToBranchConfig } if data.SafeOutputs.UploadAssets != nil { uploadConfig := map[string]any{} + // Always include max (use configured value or default of 0 for unlimited) + maxValue := 0 // default: unlimited if data.SafeOutputs.UploadAssets.Max > 0 { - uploadConfig["max"] = data.SafeOutputs.UploadAssets.Max + maxValue = data.SafeOutputs.UploadAssets.Max } + uploadConfig["max"] = maxValue safeOutputsConfig["upload_asset"] = uploadConfig } if data.SafeOutputs.MissingTool != nil { missingToolConfig := map[string]any{} + // Always include max (use configured value or default of 0 for unlimited) + maxValue := 0 // default: unlimited if data.SafeOutputs.MissingTool.Max > 0 { - missingToolConfig["max"] = data.SafeOutputs.MissingTool.Max + maxValue = data.SafeOutputs.MissingTool.Max } + missingToolConfig["max"] = maxValue safeOutputsConfig["missing_tool"] = missingToolConfig } if data.SafeOutputs.UpdateProjects != nil { updateProjectConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 10 // default if data.SafeOutputs.UpdateProjects.Max > 0 { - updateProjectConfig["max"] = data.SafeOutputs.UpdateProjects.Max + maxValue = data.SafeOutputs.UpdateProjects.Max } + updateProjectConfig["max"] = maxValue safeOutputsConfig["update_project"] = updateProjectConfig } if data.SafeOutputs.UpdateRelease != nil { updateReleaseConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.UpdateRelease.Max > 0 { - updateReleaseConfig["max"] = data.SafeOutputs.UpdateRelease.Max + maxValue = data.SafeOutputs.UpdateRelease.Max } + updateReleaseConfig["max"] = maxValue safeOutputsConfig["update_release"] = updateReleaseConfig } if data.SafeOutputs.NoOp != nil { noopConfig := map[string]any{} + // Always include max (use configured value or default) + maxValue := 1 // default if data.SafeOutputs.NoOp.Max > 0 { - noopConfig["max"] = data.SafeOutputs.NoOp.Max + maxValue = data.SafeOutputs.NoOp.Max } + noopConfig["max"] = maxValue safeOutputsConfig["noop"] = noopConfig } }