From 89b3cd952b2ada5daf151856b933d7a3f6608835 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:26:01 +0000
Subject: [PATCH 1/9] Initial plan
From 97508161c3206581460cb139b8af4f4d708ce5de Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:29:40 +0000
Subject: [PATCH 2/9] Initial plan: Add minimize-comment safe output type
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/release.lock.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index d3c78451e6b..85e445009dc 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -6272,13 +6272,13 @@ jobs:
- name: Download Go modules
run: go mod download
- name: Generate SBOM (SPDX format)
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10
+ uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0
with:
artifact-name: sbom.spdx.json
format: spdx-json
output-file: sbom.spdx.json
- name: Generate SBOM (CycloneDX format)
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10
+ uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0
with:
artifact-name: sbom.cdx.json
format: cyclonedx-json
From c386459cb73dd70a1d34565e8e285cca174b8f84 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:39:01 +0000
Subject: [PATCH 3/9] Add minimize-comment safe output type - core
implementation
- Updated JSON schema with MinimizeCommentOutput definition
- Added TypeScript types for MinimizeCommentItem
- Updated safe_outputs_tools.json with minimize_comment tool signature
- Created minimize_comment.cjs JavaScript implementation with GraphQL mutation
- Created minimize_comment.test.cjs with comprehensive tests
- Added MinimizeCommentConfig to Go types
- Created minimize_comment.go with job builder
- Updated safe_outputs.go to include minimize_comment in enabledTools and HasSafeOutputsEnabled
- Updated compiler_jobs.go to build minimize_comment job
- Integrated minimize_comment parser in extractSafeOutputsConfig
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
pkg/workflow/compiler_jobs.go | 18 ++
pkg/workflow/compiler_types.go | 1 +
pkg/workflow/js/minimize_comment.cjs | 96 ++++++++
pkg/workflow/js/minimize_comment.test.cjs | 277 ++++++++++++++++++++++
pkg/workflow/js/safe_outputs_tools.json | 15 ++
pkg/workflow/js/types/safe-outputs.d.ts | 13 +-
pkg/workflow/minimize_comment.go | 97 ++++++++
pkg/workflow/safe_outputs.go | 10 +
schemas/agent-output.json | 22 +-
9 files changed, 547 insertions(+), 2 deletions(-)
create mode 100644 pkg/workflow/js/minimize_comment.cjs
create mode 100644 pkg/workflow/js/minimize_comment.test.cjs
create mode 100644 pkg/workflow/minimize_comment.go
diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go
index 50162994eb2..7868099008a 100644
--- a/pkg/workflow/compiler_jobs.go
+++ b/pkg/workflow/compiler_jobs.go
@@ -657,6 +657,24 @@ func (c *Compiler) buildSafeOutputsJobs(data *WorkflowData, jobName, markdownPat
safeOutputJobNames = append(safeOutputJobNames, linkSubIssueJob.Name)
}
+ // Build minimize_comment job if safe-outputs.minimize-comment is configured
+ if data.SafeOutputs.MinimizeComment != nil {
+ minimizeCommentJob, err := c.buildMinimizeCommentJob(data, jobName)
+ if err != nil {
+ return fmt.Errorf("failed to build minimize_comment job: %w", err)
+ }
+ // Safe-output jobs should depend on agent job (always) AND detection job (if enabled)
+ if threatDetectionEnabled {
+ minimizeCommentJob.Needs = append(minimizeCommentJob.Needs, constants.DetectionJobName)
+ // Add detection success check to the job condition
+ minimizeCommentJob.If = AddDetectionSuccessCheck(minimizeCommentJob.If)
+ }
+ if err := c.jobManager.AddJob(minimizeCommentJob); err != nil {
+ return fmt.Errorf("failed to add minimize_comment job: %w", err)
+ }
+ safeOutputJobNames = append(safeOutputJobNames, minimizeCommentJob.Name)
+ }
+
// Build create_agent_task job if output.create-agent-task is configured
if data.SafeOutputs.CreateAgentTasks != nil {
createAgentTaskJob, err := c.buildCreateOutputAgentTaskJob(data, jobName)
diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go
index d5bc36cda88..8063b0dff6c 100644
--- a/pkg/workflow/compiler_types.go
+++ b/pkg/workflow/compiler_types.go
@@ -255,6 +255,7 @@ type SafeOutputsConfig struct {
CreateAgentTasks *CreateAgentTaskConfig `yaml:"create-agent-task,omitempty"` // Create GitHub Copilot agent tasks
UpdateProjects *UpdateProjectConfig `yaml:"update-project,omitempty"` // Smart project board management (create/add/update)
LinkSubIssue *LinkSubIssueConfig `yaml:"link-sub-issue,omitempty"` // Link issues as sub-issues
+ MinimizeComment *MinimizeCommentConfig `yaml:"minimize-comment,omitempty"` // Minimize (hide) comments
MissingTool *MissingToolConfig `yaml:"missing-tool,omitempty"` // Optional for reporting missing functionality
NoOp *NoOpConfig `yaml:"noop,omitempty"` // No-op output for logging only (always available as fallback)
ThreatDetection *ThreatDetectionConfig `yaml:"threat-detection,omitempty"` // Threat detection configuration
diff --git a/pkg/workflow/js/minimize_comment.cjs b/pkg/workflow/js/minimize_comment.cjs
new file mode 100644
index 00000000000..b9293d4c654
--- /dev/null
+++ b/pkg/workflow/js/minimize_comment.cjs
@@ -0,0 +1,96 @@
+// @ts-check
+///
+
+const { loadAgentOutput } = require("./load_agent_output.cjs");
+const { getRepositoryUrl } = require("./get_repository_url.cjs");
+
+/**
+ * Minimize (hide) a comment using the GraphQL API.
+ * @param {any} github - GitHub GraphQL instance
+ * @param {string} nodeId - Comment node ID (e.g., 'IC_kwDOABCD123456')
+ * @returns {Promise<{id: string, isMinimized: boolean}>} Minimized comment details
+ */
+async function minimizeComment(github, nodeId) {
+ const query = /* GraphQL */ `
+ mutation ($nodeId: ID!) {
+ minimizeComment(input: { subjectId: $nodeId, classifier: SPAM }) {
+ minimizedComment {
+ isMinimized
+ }
+ }
+ }
+ `;
+
+ const result = await github.graphql(query, { nodeId });
+
+ return {
+ id: nodeId,
+ isMinimized: result.minimizeComment.minimizedComment.isMinimized,
+ };
+}
+
+async function main() {
+ // Check if we're in staged mode
+ const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true";
+
+ const result = loadAgentOutput();
+ if (!result.success) {
+ return;
+ }
+
+ // Find all minimize-comment items
+ const minimizeCommentItems = result.items.filter(/** @param {any} item */ item => item.type === "minimize_comment");
+ if (minimizeCommentItems.length === 0) {
+ core.info("No minimize-comment items found in agent output");
+ return;
+ }
+
+ core.info(`Found ${minimizeCommentItems.length} minimize-comment item(s)`);
+
+ // If in staged mode, emit step summary instead of minimizing comments
+ if (isStaged) {
+ let summaryContent = "## 🎠Staged Mode: Minimize Comments Preview\n\n";
+ summaryContent += "The following comments would be minimized if staged mode was disabled:\n\n";
+
+ for (let i = 0; i < minimizeCommentItems.length; i++) {
+ const item = minimizeCommentItems[i];
+ summaryContent += `### Comment ${i + 1}\n`;
+ summaryContent += `**Node ID**: ${item.comment_id}\n`;
+ summaryContent += `**Action**: Would be minimized as SPAM\n`;
+ summaryContent += "\n";
+ }
+
+ core.summary.addRaw(summaryContent).write();
+ return;
+ }
+
+ // Process each minimize-comment item
+ for (const item of minimizeCommentItems) {
+ try {
+ const commentId = item.comment_id;
+ if (!commentId || typeof commentId !== "string") {
+ throw new Error("comment_id is required and must be a string");
+ }
+
+ core.info(`Minimizing comment: ${commentId}`);
+
+ const minimizeResult = await minimizeComment(github, commentId);
+
+ if (minimizeResult.isMinimized) {
+ core.info(`Successfully minimized comment: ${commentId}`);
+ core.setOutput("comment_id", commentId);
+ core.setOutput("is_minimized", "true");
+ } else {
+ throw new Error(`Failed to minimize comment: ${commentId}`);
+ }
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ core.error(`Failed to minimize comment: ${errorMessage}`);
+ core.setFailed(`Failed to minimize comment: ${errorMessage}`);
+ return;
+ }
+ }
+}
+
+// Call the main function
+await main();
diff --git a/pkg/workflow/js/minimize_comment.test.cjs b/pkg/workflow/js/minimize_comment.test.cjs
new file mode 100644
index 00000000000..3a3e9a10dfd
--- /dev/null
+++ b/pkg/workflow/js/minimize_comment.test.cjs
@@ -0,0 +1,277 @@
+import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
+import fs from "fs";
+import path from "path";
+
+// Mock the global objects that GitHub Actions provides
+const mockCore = {
+ debug: vi.fn(),
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ setFailed: vi.fn(),
+ setOutput: vi.fn(),
+ summary: {
+ addRaw: vi.fn().mockReturnThis(),
+ write: vi.fn().mockResolvedValue(),
+ },
+};
+
+const mockGithub = {
+ rest: {},
+ graphql: vi.fn(),
+};
+
+const mockContext = {
+ eventName: "issue_comment",
+ runId: 12345,
+ repo: {
+ owner: "testowner",
+ repo: "testrepo",
+ },
+ payload: {
+ issue: {
+ number: 42,
+ },
+ repository: {
+ html_url: "https://github.com/testowner/testrepo",
+ },
+ },
+};
+
+// Set up global mocks before importing the module
+global.core = mockCore;
+global.github = mockGithub;
+global.context = mockContext;
+
+describe("minimize_comment", () => {
+ let minimizeCommentScript;
+ let tempFilePath;
+
+ // Helper function to set agent output via file
+ const setAgentOutput = data => {
+ tempFilePath = path.join("/tmp", `test_agent_output_${Date.now()}_${Math.random().toString(36).slice(2)}.json`);
+ const content = typeof data === "string" ? data : JSON.stringify(data);
+ fs.writeFileSync(tempFilePath, content);
+ process.env.GH_AW_AGENT_OUTPUT = tempFilePath;
+ };
+
+ beforeEach(() => {
+ // Reset all mocks
+ vi.clearAllMocks();
+
+ // Reset environment variables
+ delete process.env.GH_AW_SAFE_OUTPUTS_STAGED;
+ delete process.env.GH_AW_AGENT_OUTPUT;
+ delete process.env.GITHUB_SERVER_URL;
+
+ // Reset context to default state
+ global.context.eventName = "issue_comment";
+ global.context.payload.issue = { number: 42 };
+
+ // Read the script content
+ const scriptPath = path.join(process.cwd(), "minimize_comment.cjs");
+ minimizeCommentScript = fs.readFileSync(scriptPath, "utf8");
+ });
+
+ afterEach(() => {
+ // Clean up temp files
+ if (tempFilePath && fs.existsSync(tempFilePath)) {
+ fs.unlinkSync(tempFilePath);
+ tempFilePath = undefined;
+ }
+ });
+
+ it("should handle empty agent output", async () => {
+ setAgentOutput({ items: [], errors: [] });
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.info).toHaveBeenCalledWith("No minimize-comment items found in agent output");
+ });
+
+ it("should handle missing agent output", async () => {
+ // Don't set GH_AW_AGENT_OUTPUT
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.info).toHaveBeenCalledWith("No GH_AW_AGENT_OUTPUT environment variable found");
+ });
+
+ it("should minimize a comment successfully", async () => {
+ const commentNodeId = "IC_kwDOABCD123456";
+
+ setAgentOutput({
+ items: [
+ {
+ type: "minimize_comment",
+ comment_id: commentNodeId,
+ },
+ ],
+ errors: [],
+ });
+
+ // Mock GraphQL response for minimize comment
+ mockGithub.graphql.mockResolvedValueOnce({
+ minimizeComment: {
+ minimizedComment: {
+ isMinimized: true,
+ },
+ },
+ });
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.info).toHaveBeenCalledWith("Found 1 minimize-comment item(s)");
+ expect(mockCore.info).toHaveBeenCalledWith(`Minimizing comment: ${commentNodeId}`);
+ expect(mockCore.info).toHaveBeenCalledWith(`Successfully minimized comment: ${commentNodeId}`);
+ expect(mockGithub.graphql).toHaveBeenCalledWith(
+ expect.stringContaining("minimizeComment"),
+ expect.objectContaining({ nodeId: commentNodeId })
+ );
+ expect(mockCore.setOutput).toHaveBeenCalledWith("comment_id", commentNodeId);
+ expect(mockCore.setOutput).toHaveBeenCalledWith("is_minimized", "true");
+ });
+
+ it("should handle GraphQL errors", async () => {
+ const commentNodeId = "IC_kwDOABCD123456";
+
+ setAgentOutput({
+ items: [
+ {
+ type: "minimize_comment",
+ comment_id: commentNodeId,
+ },
+ ],
+ errors: [],
+ });
+
+ // Mock GraphQL error
+ const errorMessage = "Comment not found";
+ mockGithub.graphql.mockRejectedValueOnce(new Error(errorMessage));
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.error).toHaveBeenCalledWith(expect.stringContaining(errorMessage));
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(errorMessage));
+ });
+
+ it("should preview minimization in staged mode", async () => {
+ process.env.GH_AW_SAFE_OUTPUTS_STAGED = "true";
+
+ const commentNodeId = "IC_kwDOABCD123456";
+
+ setAgentOutput({
+ items: [
+ {
+ type: "minimize_comment",
+ comment_id: commentNodeId,
+ },
+ ],
+ errors: [],
+ });
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.info).toHaveBeenCalledWith("Found 1 minimize-comment item(s)");
+ expect(mockCore.summary.addRaw).toHaveBeenCalledWith(expect.stringContaining("Staged Mode: Minimize Comments Preview"));
+ expect(mockCore.summary.addRaw).toHaveBeenCalledWith(expect.stringContaining(commentNodeId));
+ expect(mockCore.summary.write).toHaveBeenCalled();
+ expect(mockGithub.graphql).not.toHaveBeenCalled();
+ });
+
+ it("should handle multiple minimize-comment items", async () => {
+ const commentNodeId1 = "IC_kwDOABCD111111";
+ const commentNodeId2 = "IC_kwDOABCD222222";
+
+ setAgentOutput({
+ items: [
+ {
+ type: "minimize_comment",
+ comment_id: commentNodeId1,
+ },
+ {
+ type: "minimize_comment",
+ comment_id: commentNodeId2,
+ },
+ ],
+ errors: [],
+ });
+
+ // Mock GraphQL responses
+ mockGithub.graphql
+ .mockResolvedValueOnce({
+ minimizeComment: {
+ minimizedComment: {
+ isMinimized: true,
+ },
+ },
+ })
+ .mockResolvedValueOnce({
+ minimizeComment: {
+ minimizedComment: {
+ isMinimized: true,
+ },
+ },
+ });
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.info).toHaveBeenCalledWith("Found 2 minimize-comment item(s)");
+ expect(mockGithub.graphql).toHaveBeenCalledTimes(2);
+ expect(mockCore.info).toHaveBeenCalledWith(`Successfully minimized comment: ${commentNodeId1}`);
+ expect(mockCore.info).toHaveBeenCalledWith(`Successfully minimized comment: ${commentNodeId2}`);
+ });
+
+ it("should fail when comment_id is missing", async () => {
+ setAgentOutput({
+ items: [
+ {
+ type: "minimize_comment",
+ // Missing comment_id
+ },
+ ],
+ errors: [],
+ });
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.error).toHaveBeenCalledWith(expect.stringContaining("comment_id is required"));
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("comment_id is required"));
+ });
+
+ it("should fail when minimize returns false", async () => {
+ const commentNodeId = "IC_kwDOABCD123456";
+
+ setAgentOutput({
+ items: [
+ {
+ type: "minimize_comment",
+ comment_id: commentNodeId,
+ },
+ ],
+ errors: [],
+ });
+
+ // Mock GraphQL response where minimize fails
+ mockGithub.graphql.mockResolvedValueOnce({
+ minimizeComment: {
+ minimizedComment: {
+ isMinimized: false,
+ },
+ },
+ });
+
+ // Execute the script
+ await eval(`(async () => { ${minimizeCommentScript} })()`);
+
+ expect(mockCore.error).toHaveBeenCalledWith(expect.stringContaining("Failed to minimize comment"));
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("Failed to minimize comment"));
+ });
+});
diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json
index 89069a558a8..a62761d83fd 100644
--- a/pkg/workflow/js/safe_outputs_tools.json
+++ b/pkg/workflow/js/safe_outputs_tools.json
@@ -530,5 +530,20 @@
},
"additionalProperties": false
}
+ },
+ {
+ "name": "minimize_comment",
+ "description": "Minimize (hide) a comment on a GitHub issue, pull request, or discussion. This collapses the comment as spam or off-topic. Use this for inappropriate, off-topic, or outdated comments. To find a comment's node ID, use GraphQL queries or the GitHub API.",
+ "inputSchema": {
+ "type": "object",
+ "required": ["comment_id"],
+ "properties": {
+ "comment_id": {
+ "type": ["number", "string"],
+ "description": "Node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456'). This is the GraphQL node ID, not the numeric comment ID. Can be obtained from GraphQL queries or comment API responses."
+ }
+ },
+ "additionalProperties": false
+ }
}
]
diff --git a/pkg/workflow/js/types/safe-outputs.d.ts b/pkg/workflow/js/types/safe-outputs.d.ts
index 07c412f1153..121050bb630 100644
--- a/pkg/workflow/js/types/safe-outputs.d.ts
+++ b/pkg/workflow/js/types/safe-outputs.d.ts
@@ -276,6 +276,15 @@ interface LinkSubIssueItem extends BaseSafeOutputItem {
sub_issue_number: number | string;
}
+/**
+ * JSONL item for minimizing (hiding) a comment
+ */
+interface MinimizeCommentItem extends BaseSafeOutputItem {
+ type: "minimize_comment";
+ /** Node ID of the comment to minimize */
+ comment_id: number | string;
+}
+
/**
* Union type of all possible safe output items
*/
@@ -300,7 +309,8 @@ type SafeOutputItem =
| AssignToAgentItem
| UpdateReleaseItem
| NoOpItem
- | LinkSubIssueItem;
+ | LinkSubIssueItem
+ | MinimizeCommentItem;
/**
* Sanitized safe output items
@@ -334,6 +344,7 @@ export {
UpdateReleaseItem,
NoOpItem,
LinkSubIssueItem,
+ MinimizeCommentItem,
SafeOutputItem,
SafeOutputItems,
};
diff --git a/pkg/workflow/minimize_comment.go b/pkg/workflow/minimize_comment.go
new file mode 100644
index 00000000000..6182aca6b44
--- /dev/null
+++ b/pkg/workflow/minimize_comment.go
@@ -0,0 +1,97 @@
+package workflow
+
+import (
+ "fmt"
+
+ "github.com/githubnext/gh-aw/pkg/logger"
+)
+
+var minimizeCommentLog = logger.New("workflow:minimize_comment")
+
+// MinimizeCommentConfig holds configuration for minimizing (hiding) comments from agent output
+type MinimizeCommentConfig struct {
+ BaseSafeOutputConfig `yaml:",inline"`
+ SafeOutputTargetConfig `yaml:",inline"`
+}
+
+// parseMinimizeCommentConfig handles minimize-comment configuration
+func (c *Compiler) parseMinimizeCommentConfig(outputMap map[string]any) *MinimizeCommentConfig {
+ minimizeCommentLog.Print("Parsing minimize-comment configuration")
+ if configData, exists := outputMap["minimize-comment"]; exists {
+ minimizeCommentConfig := &MinimizeCommentConfig{}
+
+ if configMap, ok := configData.(map[string]any); ok {
+ minimizeCommentLog.Print("Found minimize-comment config map")
+
+ // Parse target config (target-repo) with validation
+ targetConfig, isInvalid := ParseTargetConfig(configMap)
+ if isInvalid {
+ return nil // Invalid configuration (e.g., wildcard target-repo), return nil to cause validation error
+ }
+ minimizeCommentConfig.SafeOutputTargetConfig = targetConfig
+
+ // Parse common base fields with default max of 5
+ c.parseBaseSafeOutputConfig(configMap, &minimizeCommentConfig.BaseSafeOutputConfig, 5)
+
+ minimizeCommentLog.Printf("Parsed minimize-comment config: max=%d, target_repo=%s",
+ minimizeCommentConfig.Max, minimizeCommentConfig.TargetRepoSlug)
+ } else {
+ // If configData is nil or not a map, still set the default max
+ minimizeCommentConfig.Max = 5
+ }
+
+ return minimizeCommentConfig
+ }
+
+ return nil
+}
+
+// buildMinimizeCommentJob creates the minimize_comment job
+func (c *Compiler) buildMinimizeCommentJob(data *WorkflowData, mainJobName string) (*Job, error) {
+ minimizeCommentLog.Printf("Building minimize_comment job: main_job=%s", mainJobName)
+ if data.SafeOutputs == nil || data.SafeOutputs.MinimizeComment == nil {
+ return nil, fmt.Errorf("safe-outputs.minimize-comment configuration is required")
+ }
+
+ cfg := data.SafeOutputs.MinimizeComment
+
+ maxCount := 5
+ if cfg.Max > 0 {
+ maxCount = cfg.Max
+ }
+
+ // Build custom environment variables specific to minimize-comment
+ var customEnvVars []string
+
+ // Pass the max limit
+ customEnvVars = append(customEnvVars, BuildMaxCountEnvVar("GH_AW_MINIMIZE_COMMENT_MAX_COUNT", maxCount)...)
+
+ // Add standard environment variables (metadata + staged/target repo)
+ customEnvVars = append(customEnvVars, c.buildStandardSafeOutputEnvVars(data, cfg.TargetRepoSlug)...)
+
+ // Create outputs for the job
+ outputs := map[string]string{
+ "comment_id": "${{ steps.minimize_comment.outputs.comment_id }}",
+ "is_minimized": "${{ steps.minimize_comment.outputs.is_minimized }}",
+ }
+
+ // Use the shared builder function to create the job
+ return c.buildSafeOutputJob(data, SafeOutputJobConfig{
+ JobName: "minimize_comment",
+ StepName: "Minimize Comment",
+ StepID: "minimize_comment",
+ MainJobName: mainJobName,
+ CustomEnvVars: customEnvVars,
+ Script: getMinimizeCommentScript(),
+ Permissions: NewPermissionsContentsReadIssuesWrite(),
+ Outputs: outputs,
+ Token: cfg.GitHubToken,
+ Condition: BuildSafeOutputType("minimize_comment"),
+ TargetRepoSlug: cfg.TargetRepoSlug,
+ })
+}
+
+// getMinimizeCommentScript returns the JavaScript implementation
+func getMinimizeCommentScript() string {
+ return embedJavaScript("minimize_comment.cjs")
+}
diff --git a/pkg/workflow/safe_outputs.go b/pkg/workflow/safe_outputs.go
index e854b979ad9..4c5f200e15c 100644
--- a/pkg/workflow/safe_outputs.go
+++ b/pkg/workflow/safe_outputs.go
@@ -50,6 +50,7 @@ func HasSafeOutputsEnabled(safeOutputs *SafeOutputsConfig) bool {
safeOutputs.MissingTool != nil ||
safeOutputs.NoOp != nil ||
safeOutputs.LinkSubIssue != nil ||
+ safeOutputs.MinimizeComment != nil ||
len(safeOutputs.Jobs) > 0
if safeOutputsLog.Enabled() {
@@ -218,6 +219,12 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut
config.LinkSubIssue = linkSubIssueConfig
}
+ // Handle minimize-comment
+ minimizeCommentConfig := c.parseMinimizeCommentConfig(outputMap)
+ if minimizeCommentConfig != nil {
+ config.MinimizeComment = minimizeCommentConfig
+ }
+
// Handle missing-tool (parse configuration if present, or enable by default)
missingToolConfig := c.parseMissingToolConfig(outputMap)
if missingToolConfig != nil {
@@ -1105,6 +1112,9 @@ func generateFilteredToolsJSON(data *WorkflowData) (string, error) {
if data.SafeOutputs.LinkSubIssue != nil {
enabledTools["link_sub_issue"] = true
}
+ if data.SafeOutputs.MinimizeComment != nil {
+ enabledTools["minimize_comment"] = true
+ }
// Filter tools to only include enabled ones and enhance descriptions
var filteredTools []map[string]any
diff --git a/schemas/agent-output.json b/schemas/agent-output.json
index 4cc6fd80f79..54cc7a4e625 100644
--- a/schemas/agent-output.json
+++ b/schemas/agent-output.json
@@ -47,7 +47,8 @@
{"$ref": "#/$defs/AssignMilestoneOutput"},
{"$ref": "#/$defs/AssignToAgentOutput"},
{"$ref": "#/$defs/NoOpOutput"},
- {"$ref": "#/$defs/LinkSubIssueOutput"}
+ {"$ref": "#/$defs/LinkSubIssueOutput"},
+ {"$ref": "#/$defs/MinimizeCommentOutput"}
]
},
"CreateIssueOutput": {
@@ -622,6 +623,25 @@
},
"required": ["type", "parent_issue_number", "sub_issue_number"],
"additionalProperties": false
+ },
+ "MinimizeCommentOutput": {
+ "title": "Minimize Comment Output",
+ "description": "Output for minimizing (hiding) a comment on a GitHub issue, pull request, or discussion",
+ "type": "object",
+ "properties": {
+ "type": {
+ "const": "minimize_comment"
+ },
+ "comment_id": {
+ "oneOf": [
+ {"type": "number"},
+ {"type": "string"}
+ ],
+ "description": "Node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456')"
+ }
+ },
+ "required": ["type", "comment_id"],
+ "additionalProperties": false
}
}
}
\ No newline at end of file
From c7c5556e6436f6bcdb285f612f1212e6663fadf7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:46:08 +0000
Subject: [PATCH 4/9] Fix test: Update expected tools list with
minimize_comment
- Added minimize_comment to expectedTools list in safe_outputs_tools_test.go
- Fixed script registration in scripts.go
- All tests now passing
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/release.lock.yml | 4 ++--
pkg/workflow/minimize_comment.go | 2 +-
pkg/workflow/safe_outputs_tools_test.go | 1 +
pkg/workflow/scripts.go | 4 ++++
4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index 85e445009dc..d3c78451e6b 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -6272,13 +6272,13 @@ jobs:
- name: Download Go modules
run: go mod download
- name: Generate SBOM (SPDX format)
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0
+ uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10
with:
artifact-name: sbom.spdx.json
format: spdx-json
output-file: sbom.spdx.json
- name: Generate SBOM (CycloneDX format)
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0
+ uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10
with:
artifact-name: sbom.cdx.json
format: cyclonedx-json
diff --git a/pkg/workflow/minimize_comment.go b/pkg/workflow/minimize_comment.go
index 6182aca6b44..643ba79b246 100644
--- a/pkg/workflow/minimize_comment.go
+++ b/pkg/workflow/minimize_comment.go
@@ -93,5 +93,5 @@ func (c *Compiler) buildMinimizeCommentJob(data *WorkflowData, mainJobName strin
// getMinimizeCommentScript returns the JavaScript implementation
func getMinimizeCommentScript() string {
- return embedJavaScript("minimize_comment.cjs")
+ return DefaultScriptRegistry.GetWithMode("minimize_comment", RuntimeModeGitHubScript)
}
diff --git a/pkg/workflow/safe_outputs_tools_test.go b/pkg/workflow/safe_outputs_tools_test.go
index bae78ef1abe..8477f57fcc1 100644
--- a/pkg/workflow/safe_outputs_tools_test.go
+++ b/pkg/workflow/safe_outputs_tools_test.go
@@ -291,6 +291,7 @@ func TestGetSafeOutputsToolsJSON(t *testing.T) {
"upload_asset",
"update_release",
"link_sub_issue",
+ "minimize_comment",
"missing_tool",
"noop",
}
diff --git a/pkg/workflow/scripts.go b/pkg/workflow/scripts.go
index 3c9b264a755..82a32f175e6 100644
--- a/pkg/workflow/scripts.go
+++ b/pkg/workflow/scripts.go
@@ -52,6 +52,9 @@ var assignCopilotToCreatedIssuesScriptSource string
//go:embed js/link_sub_issue.cjs
var linkSubIssueScriptSource string
+//go:embed js/minimize_comment.cjs
+var minimizeCommentScriptSource string
+
//go:embed js/create_discussion.cjs
var createDiscussionScriptSource string
@@ -147,6 +150,7 @@ func init() {
DefaultScriptRegistry.Register("assign_to_user", assignToUserScriptSource)
DefaultScriptRegistry.Register("assign_copilot_to_created_issues", assignCopilotToCreatedIssuesScriptSource)
DefaultScriptRegistry.Register("link_sub_issue", linkSubIssueScriptSource)
+ DefaultScriptRegistry.Register("minimize_comment", minimizeCommentScriptSource)
DefaultScriptRegistry.Register("create_discussion", createDiscussionScriptSource)
DefaultScriptRegistry.Register("close_discussion", closeDiscussionScriptSource)
DefaultScriptRegistry.Register("close_expired_discussions", closeExpiredDiscussionsScriptSource)
From 4b90e8de05fdac406ab2aa2237f23ea90e021571 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:55:15 +0000
Subject: [PATCH 5/9] Add minimize-comment to workflow schema and create test
workflows
- Updated main_workflow_schema.json with minimize-comment configuration
- Created test workflows for copilot, claude, and codex engines
- All tests passing
- Workflows compile successfully
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.../workflows/test-claude-minimize-comment.md | 19 ++++++++++++
.../workflows/test-codex-minimize-comment.md | 19 ++++++++++++
.../test-copilot-minimize-comment.md | 19 ++++++++++++
pkg/parser/schemas/main_workflow_schema.json | 31 ++++++++++++++++++-
4 files changed, 87 insertions(+), 1 deletion(-)
create mode 100644 pkg/cli/workflows/test-claude-minimize-comment.md
create mode 100644 pkg/cli/workflows/test-codex-minimize-comment.md
create mode 100644 pkg/cli/workflows/test-copilot-minimize-comment.md
diff --git a/pkg/cli/workflows/test-claude-minimize-comment.md b/pkg/cli/workflows/test-claude-minimize-comment.md
new file mode 100644
index 00000000000..089a812f251
--- /dev/null
+++ b/pkg/cli/workflows/test-claude-minimize-comment.md
@@ -0,0 +1,19 @@
+---
+on:
+ workflow_dispatch:
+engine: claude
+safe-outputs:
+ minimize-comment:
+ max: 3
+timeout-minutes: 5
+---
+
+# Test Claude Minimize Comment
+
+This is a test workflow to verify that Claude can minimize (hide) comments on GitHub issues.
+
+Test the minimize_comment safe output by minimizing a comment with the following node ID:
+
+- comment_id: "IC_kwDOABCD123456"
+
+Output the minimize-comment action as JSONL format using the minimize_comment tool.
diff --git a/pkg/cli/workflows/test-codex-minimize-comment.md b/pkg/cli/workflows/test-codex-minimize-comment.md
new file mode 100644
index 00000000000..249cf394fbe
--- /dev/null
+++ b/pkg/cli/workflows/test-codex-minimize-comment.md
@@ -0,0 +1,19 @@
+---
+on:
+ workflow_dispatch:
+engine: codex
+safe-outputs:
+ minimize-comment:
+ max: 3
+timeout-minutes: 5
+---
+
+# Test Codex Minimize Comment
+
+This is a test workflow to verify that Codex can minimize (hide) comments on GitHub issues.
+
+Test the minimize_comment safe output by minimizing a comment with the following node ID:
+
+- comment_id: "IC_kwDOABCD123456"
+
+Output the minimize-comment action as JSONL format using the minimize_comment tool.
diff --git a/pkg/cli/workflows/test-copilot-minimize-comment.md b/pkg/cli/workflows/test-copilot-minimize-comment.md
new file mode 100644
index 00000000000..97b9c56e928
--- /dev/null
+++ b/pkg/cli/workflows/test-copilot-minimize-comment.md
@@ -0,0 +1,19 @@
+---
+on:
+ workflow_dispatch:
+engine: copilot
+safe-outputs:
+ minimize-comment:
+ max: 3
+timeout-minutes: 5
+---
+
+# Test Copilot Minimize Comment
+
+This is a test workflow to verify that Copilot can minimize (hide) comments on GitHub issues.
+
+Test the minimize_comment safe output by minimizing a comment with the following node ID:
+
+- comment_id: "IC_kwDOABCD123456"
+
+Output the minimize-comment action as JSONL format using the minimize_comment tool.
diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json
index f3f90ec282d..4e5dfb424c4 100644
--- a/pkg/parser/schemas/main_workflow_schema.json
+++ b/pkg/parser/schemas/main_workflow_schema.json
@@ -2988,7 +2988,7 @@
"safe-outputs": {
"type": "object",
"description": "Safe output processing configuration that automatically creates GitHub issues, comments, and pull requests from AI workflow output without requiring write permissions in the main job",
- "$comment": "Required if workflow creates or modifies GitHub resources. Operations requiring safe-outputs: add-comment, add-labels, add-reviewer, assign-milestone, assign-to-agent, close-discussion, close-issue, close-pull-request, create-agent-task, create-code-scanning-alert, create-discussion, create-issue, create-pull-request, create-pull-request-review-comment, link-sub-issue, missing-tool, noop, push-to-pull-request-branch, threat-detection, update-issue, update-project, update-pull-request, update-release, upload-asset. See documentation for complete details.",
+ "$comment": "Required if workflow creates or modifies GitHub resources. Operations requiring safe-outputs: add-comment, add-labels, add-reviewer, assign-milestone, assign-to-agent, close-discussion, close-issue, close-pull-request, create-agent-task, create-code-scanning-alert, create-discussion, create-issue, create-pull-request, create-pull-request-review-comment, link-sub-issue, minimize-comment, missing-tool, noop, push-to-pull-request-branch, threat-detection, update-issue, update-project, update-pull-request, update-release, upload-asset. See documentation for complete details.",
"properties": {
"allowed-domains": {
"type": "array",
@@ -4013,6 +4013,35 @@
}
]
},
+ "minimize-comment": {
+ "oneOf": [
+ {
+ "type": "null",
+ "description": "Enable comment minimization with default configuration"
+ },
+ {
+ "type": "object",
+ "description": "Configuration for minimizing (hiding) comments on GitHub issues, pull requests, or discussions from agentic workflow output",
+ "properties": {
+ "max": {
+ "type": "integer",
+ "description": "Maximum number of comments to minimize (default: 5)",
+ "minimum": 1,
+ "maximum": 100
+ },
+ "target-repo": {
+ "type": "string",
+ "description": "Target repository in format 'owner/repo' for cross-repository comment minimization. Takes precedence over trial target repo settings."
+ },
+ "github-token": {
+ "$ref": "#/$defs/github_token",
+ "description": "GitHub token to use for this specific output type. Overrides global github-token if specified."
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
"missing-tool": {
"oneOf": [
{
From c299bf8df79efdcd456bc3b2c3c7c9cc5f1e85e6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:57:19 +0000
Subject: [PATCH 6/9] Address code review feedback
- Remove unused getRepositoryUrl import from minimize_comment.cjs
- Add support for numeric comment_id values (convert to string for GraphQL)
- Improve error messages to be more descriptive
- All tests still passing
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
pkg/workflow/js/minimize_comment.cjs | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/pkg/workflow/js/minimize_comment.cjs b/pkg/workflow/js/minimize_comment.cjs
index b9293d4c654..31b6b7c9493 100644
--- a/pkg/workflow/js/minimize_comment.cjs
+++ b/pkg/workflow/js/minimize_comment.cjs
@@ -2,7 +2,6 @@
///
const { loadAgentOutput } = require("./load_agent_output.cjs");
-const { getRepositoryUrl } = require("./get_repository_url.cjs");
/**
* Minimize (hide) a comment using the GraphQL API.
@@ -68,20 +67,27 @@ async function main() {
for (const item of minimizeCommentItems) {
try {
const commentId = item.comment_id;
- if (!commentId || typeof commentId !== "string") {
- throw new Error("comment_id is required and must be a string");
+ if (!commentId) {
+ throw new Error("comment_id is required");
}
- core.info(`Minimizing comment: ${commentId}`);
+ // Convert numeric comment_id to string if needed (GraphQL requires string node IDs)
+ const commentNodeId = typeof commentId === "number" ? String(commentId) : commentId;
- const minimizeResult = await minimizeComment(github, commentId);
+ if (typeof commentNodeId !== "string") {
+ throw new Error("comment_id must be a string or number");
+ }
+
+ core.info(`Minimizing comment: ${commentNodeId}`);
+
+ const minimizeResult = await minimizeComment(github, commentNodeId);
if (minimizeResult.isMinimized) {
- core.info(`Successfully minimized comment: ${commentId}`);
- core.setOutput("comment_id", commentId);
+ core.info(`Successfully minimized comment: ${commentNodeId}`);
+ core.setOutput("comment_id", commentNodeId);
core.setOutput("is_minimized", "true");
} else {
- throw new Error(`Failed to minimize comment: ${commentId}`);
+ throw new Error(`Failed to minimize comment: ${commentNodeId}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
From ecda91ac28a50af6c50c31d76d50c125b3bde69b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 22:59:44 +0000
Subject: [PATCH 7/9] Fix permissions: Use full permissions for
minimize_comment
- Changed from NewPermissionsContentsReadIssuesWrite() to
NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()
- Matches broader scope of minimize_comment (works on issues, PRs, and discussions)
- Aligns with add_comment permissions pattern
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
pkg/workflow/minimize_comment.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/workflow/minimize_comment.go b/pkg/workflow/minimize_comment.go
index 643ba79b246..828f070d84b 100644
--- a/pkg/workflow/minimize_comment.go
+++ b/pkg/workflow/minimize_comment.go
@@ -83,7 +83,7 @@ func (c *Compiler) buildMinimizeCommentJob(data *WorkflowData, mainJobName strin
MainJobName: mainJobName,
CustomEnvVars: customEnvVars,
Script: getMinimizeCommentScript(),
- Permissions: NewPermissionsContentsReadIssuesWrite(),
+ Permissions: NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite(),
Outputs: outputs,
Token: cfg.GitHubToken,
Condition: BuildSafeOutputType("minimize_comment"),
From 02df0ea2713dd42ae25bcaea4423809a60a5ad77 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 23:02:45 +0000
Subject: [PATCH 8/9] Restrict comment_id to string type only (GraphQL node
IDs)
- Changed comment_id from (number|string) to string only in all schemas
- GraphQL node IDs are always strings (e.g., 'IC_kwDOABCD123456')
- Numeric REST API comment IDs cannot be converted to GraphQL node IDs
- Updated error message to clarify GraphQL node ID requirement
- Updated tool description to emphasize GraphQL node ID vs REST comment ID
- All tests still passing
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
pkg/workflow/js/minimize_comment.cjs | 21 +++++++--------------
pkg/workflow/js/safe_outputs_tools.json | 6 +++---
pkg/workflow/js/types/safe-outputs.d.ts | 4 ++--
schemas/agent-output.json | 8 +++-----
4 files changed, 15 insertions(+), 24 deletions(-)
diff --git a/pkg/workflow/js/minimize_comment.cjs b/pkg/workflow/js/minimize_comment.cjs
index 31b6b7c9493..575110ed001 100644
--- a/pkg/workflow/js/minimize_comment.cjs
+++ b/pkg/workflow/js/minimize_comment.cjs
@@ -67,27 +67,20 @@ async function main() {
for (const item of minimizeCommentItems) {
try {
const commentId = item.comment_id;
- if (!commentId) {
- throw new Error("comment_id is required");
+ if (!commentId || typeof commentId !== "string") {
+ throw new Error("comment_id is required and must be a string (GraphQL node ID)");
}
- // Convert numeric comment_id to string if needed (GraphQL requires string node IDs)
- const commentNodeId = typeof commentId === "number" ? String(commentId) : commentId;
+ core.info(`Minimizing comment: ${commentId}`);
- if (typeof commentNodeId !== "string") {
- throw new Error("comment_id must be a string or number");
- }
-
- core.info(`Minimizing comment: ${commentNodeId}`);
-
- const minimizeResult = await minimizeComment(github, commentNodeId);
+ const minimizeResult = await minimizeComment(github, commentId);
if (minimizeResult.isMinimized) {
- core.info(`Successfully minimized comment: ${commentNodeId}`);
- core.setOutput("comment_id", commentNodeId);
+ core.info(`Successfully minimized comment: ${commentId}`);
+ core.setOutput("comment_id", commentId);
core.setOutput("is_minimized", "true");
} else {
- throw new Error(`Failed to minimize comment: ${commentNodeId}`);
+ throw new Error(`Failed to minimize comment: ${commentId}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json
index a62761d83fd..e9d9d6fd69f 100644
--- a/pkg/workflow/js/safe_outputs_tools.json
+++ b/pkg/workflow/js/safe_outputs_tools.json
@@ -533,14 +533,14 @@
},
{
"name": "minimize_comment",
- "description": "Minimize (hide) a comment on a GitHub issue, pull request, or discussion. This collapses the comment as spam or off-topic. Use this for inappropriate, off-topic, or outdated comments. To find a comment's node ID, use GraphQL queries or the GitHub API.",
+ "description": "Minimize (hide) a comment on a GitHub issue, pull request, or discussion. This collapses the comment as spam or off-topic. Use this for inappropriate, off-topic, or outdated comments. The comment_id must be a GraphQL node ID (string like 'IC_kwDOABCD123456'), not a numeric REST API comment ID.",
"inputSchema": {
"type": "object",
"required": ["comment_id"],
"properties": {
"comment_id": {
- "type": ["number", "string"],
- "description": "Node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456'). This is the GraphQL node ID, not the numeric comment ID. Can be obtained from GraphQL queries or comment API responses."
+ "type": "string",
+ "description": "GraphQL node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456'). This is the GraphQL node ID, not the numeric comment ID from REST API. Can be obtained from GraphQL queries or comment API responses."
}
},
"additionalProperties": false
diff --git a/pkg/workflow/js/types/safe-outputs.d.ts b/pkg/workflow/js/types/safe-outputs.d.ts
index 121050bb630..7cfa8710036 100644
--- a/pkg/workflow/js/types/safe-outputs.d.ts
+++ b/pkg/workflow/js/types/safe-outputs.d.ts
@@ -281,8 +281,8 @@ interface LinkSubIssueItem extends BaseSafeOutputItem {
*/
interface MinimizeCommentItem extends BaseSafeOutputItem {
type: "minimize_comment";
- /** Node ID of the comment to minimize */
- comment_id: number | string;
+ /** GraphQL node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456') */
+ comment_id: string;
}
/**
diff --git a/schemas/agent-output.json b/schemas/agent-output.json
index 54cc7a4e625..f3f0e5a7a37 100644
--- a/schemas/agent-output.json
+++ b/schemas/agent-output.json
@@ -633,11 +633,9 @@
"const": "minimize_comment"
},
"comment_id": {
- "oneOf": [
- {"type": "number"},
- {"type": "string"}
- ],
- "description": "Node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456')"
+ "type": "string",
+ "description": "Node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456')",
+ "minLength": 1
}
},
"required": ["type", "comment_id"],
From decede30b4f5db639a77568f1c85d5a20546b863 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Dec 2025 23:12:59 +0000
Subject: [PATCH 9/9] Add minimize-comment to smoke-codex workflow
- Added minimize-comment safe output to smoke-codex.md
- Recompiled smoke-codex.lock.yml with minimize_comment job
- All test workflows already created in pkg/cli/workflows
- ScriptRegistry already has minimize_comment registered
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/release.lock.yml | 4 +-
.github/workflows/smoke-codex.lock.yml | 173 +++++++++++++++++++++++++
.github/workflows/smoke-codex.md | 1 +
3 files changed, 176 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index d3c78451e6b..85e445009dc 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -6272,13 +6272,13 @@ jobs:
- name: Download Go modules
run: go mod download
- name: Generate SBOM (SPDX format)
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10
+ uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0
with:
artifact-name: sbom.spdx.json
format: spdx-json
output-file: sbom.spdx.json
- name: Generate SBOM (CycloneDX format)
- uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10
+ uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0
with:
artifact-name: sbom.cdx.json
format: cyclonedx-json
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index da0212aa524..0098654360c 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -58,6 +58,7 @@
# create-issue:
# add-labels:
# allowed: [smoke-codex]
+# minimize-comment:
# messages:
# footer: "> 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*"
# run-started: "🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}..."
@@ -76,6 +77,7 @@
# conclusion["conclusion"]
# create_issue["create_issue"]
# detection["detection"]
+# minimize_comment["minimize_comment"]
# pre_activation["pre_activation"]
# update_cache_memory["update_cache_memory"]
# activation --> agent
@@ -87,6 +89,7 @@
# agent --> conclusion
# agent --> create_issue
# agent --> detection
+# agent --> minimize_comment
# agent --> update_cache_memory
# create_issue --> add_comment
# create_issue --> conclusion
@@ -94,7 +97,9 @@
# detection --> add_labels
# detection --> conclusion
# detection --> create_issue
+# detection --> minimize_comment
# detection --> update_cache_memory
+# minimize_comment --> conclusion
# pre_activation --> activation
# update_cache_memory --> conclusion
# ```
@@ -2098,6 +2103,23 @@ jobs:
"type": "object"
},
"name": "noop"
+ },
+ {
+ "description": "Minimize (hide) a comment on a GitHub issue, pull request, or discussion. This collapses the comment as spam or off-topic. Use this for inappropriate, off-topic, or outdated comments. The comment_id must be a GraphQL node ID (string like 'IC_kwDOABCD123456'), not a numeric REST API comment ID.",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "comment_id": {
+ "description": "GraphQL node ID of the comment to minimize (e.g., 'IC_kwDOABCD123456'). This is the GraphQL node ID, not the numeric comment ID from REST API. Can be obtained from GraphQL queries or comment API responses.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "comment_id"
+ ],
+ "type": "object"
+ },
+ "name": "minimize_comment"
}
]
EOF
@@ -6415,6 +6437,7 @@ jobs:
- agent
- create_issue
- detection
+ - minimize_comment
- update_cache_memory
if: ((always()) && (needs.agent.result != 'skipped')) && (!(needs.add_comment.outputs.comment_id))
runs-on: ubuntu-slim
@@ -7732,6 +7755,156 @@ jobs:
path: /tmp/gh-aw/threat-detection/detection.log
if-no-files-found: ignore
+ minimize_comment:
+ needs:
+ - agent
+ - detection
+ if: >
+ (((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'minimize_comment'))) &&
+ (needs.detection.outputs.success == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ pull-requests: write
+ timeout-minutes: 10
+ outputs:
+ comment_id: ${{ steps.minimize_comment.outputs.comment_id }}
+ is_minimized: ${{ steps.minimize_comment.outputs.is_minimized }}
+ steps:
+ - name: Download agent output artifact
+ continue-on-error: true
+ uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
+ with:
+ name: agent_output.json
+ path: /tmp/gh-aw/safeoutputs/
+ - name: Setup agent output environment variable
+ run: |
+ mkdir -p /tmp/gh-aw/safeoutputs/
+ find "/tmp/gh-aw/safeoutputs/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
+ - name: Minimize Comment
+ id: minimize_comment
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MINIMIZE_COMMENT_MAX_COUNT: 5
+ GH_AW_WORKFLOW_NAME: "Smoke Codex"
+ GH_AW_ENGINE_ID: "codex"
+ GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*\",\"runStarted\":\"🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}...\",\"runSuccess\":\"✨ The prophecy is fulfilled... [{workflow_name}]({run_url}) has completed its mystical journey. The stars align. 🌟\",\"runFailure\":\"🌑 The shadows whisper... [{workflow_name}]({run_url}) {status}. The oracle requires further meditation...\"}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const fs = require("fs");
+ const MAX_LOG_CONTENT_LENGTH = 10000;
+ function truncateForLogging(content) {
+ if (content.length <= MAX_LOG_CONTENT_LENGTH) {
+ return content;
+ }
+ return content.substring(0, MAX_LOG_CONTENT_LENGTH) + `\n... (truncated, total length: ${content.length})`;
+ }
+ function loadAgentOutput() {
+ const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
+ if (!agentOutputFile) {
+ core.info("No GH_AW_AGENT_OUTPUT environment variable found");
+ return { success: false };
+ }
+ let outputContent;
+ try {
+ outputContent = fs.readFileSync(agentOutputFile, "utf8");
+ } catch (error) {
+ const errorMessage = `Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`;
+ core.error(errorMessage);
+ return { success: false, error: errorMessage };
+ }
+ if (outputContent.trim() === "") {
+ core.info("Agent output content is empty");
+ return { success: false };
+ }
+ core.info(`Agent output content length: ${outputContent.length}`);
+ let validatedOutput;
+ try {
+ validatedOutput = JSON.parse(outputContent);
+ } catch (error) {
+ const errorMessage = `Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`;
+ core.error(errorMessage);
+ core.info(`Failed to parse content:\n${truncateForLogging(outputContent)}`);
+ return { success: false, error: errorMessage };
+ }
+ if (!validatedOutput.items || !Array.isArray(validatedOutput.items)) {
+ core.info("No valid items found in agent output");
+ core.info(`Parsed content: ${truncateForLogging(JSON.stringify(validatedOutput))}`);
+ return { success: false };
+ }
+ return { success: true, items: validatedOutput.items };
+ }
+ async function minimizeComment(github, nodeId) {
+ const query = `
+ mutation ($nodeId: ID!) {
+ minimizeComment(input: { subjectId: $nodeId, classifier: SPAM }) {
+ minimizedComment {
+ isMinimized
+ }
+ }
+ }
+ `;
+ const result = await github.graphql(query, { nodeId });
+ return {
+ id: nodeId,
+ isMinimized: result.minimizeComment.minimizedComment.isMinimized,
+ };
+ }
+ async function main() {
+ const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true";
+ const result = loadAgentOutput();
+ if (!result.success) {
+ return;
+ }
+ const minimizeCommentItems = result.items.filter( item => item.type === "minimize_comment");
+ if (minimizeCommentItems.length === 0) {
+ core.info("No minimize-comment items found in agent output");
+ return;
+ }
+ core.info(`Found ${minimizeCommentItems.length} minimize-comment item(s)`);
+ if (isStaged) {
+ let summaryContent = "## 🎠Staged Mode: Minimize Comments Preview\n\n";
+ summaryContent += "The following comments would be minimized if staged mode was disabled:\n\n";
+ for (let i = 0; i < minimizeCommentItems.length; i++) {
+ const item = minimizeCommentItems[i];
+ summaryContent += `### Comment ${i + 1}\n`;
+ summaryContent += `**Node ID**: ${item.comment_id}\n`;
+ summaryContent += `**Action**: Would be minimized as SPAM\n`;
+ summaryContent += "\n";
+ }
+ core.summary.addRaw(summaryContent).write();
+ return;
+ }
+ for (const item of minimizeCommentItems) {
+ try {
+ const commentId = item.comment_id;
+ if (!commentId || typeof commentId !== "string") {
+ throw new Error("comment_id is required and must be a string (GraphQL node ID)");
+ }
+ core.info(`Minimizing comment: ${commentId}`);
+ const minimizeResult = await minimizeComment(github, commentId);
+ if (minimizeResult.isMinimized) {
+ core.info(`Successfully minimized comment: ${commentId}`);
+ core.setOutput("comment_id", commentId);
+ core.setOutput("is_minimized", "true");
+ } else {
+ throw new Error(`Failed to minimize comment: ${commentId}`);
+ }
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ core.error(`Failed to minimize comment: ${errorMessage}`);
+ core.setFailed(`Failed to minimize comment: ${errorMessage}`);
+ return;
+ }
+ }
+ }
+ await main();
+
pre_activation:
if: >
((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.id == github.repository_id)) &&
diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md
index 9a5f4783721..7574f2be2e1 100644
--- a/.github/workflows/smoke-codex.md
+++ b/.github/workflows/smoke-codex.md
@@ -35,6 +35,7 @@ safe-outputs:
create-issue:
add-labels:
allowed: [smoke-codex]
+ minimize-comment:
messages:
footer: "> 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*"
run-started: "🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}..."