From ceabdea96bc465fb5fe064382cb035bab7615afa Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 12 Feb 2026 18:27:00 +0000
Subject: [PATCH 1/8] Initial plan
From 3391cbe6942ecae2fe930cd2ca2fb9eec0f1b147 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 12 Feb 2026 18:35:58 +0000
Subject: [PATCH 2/8] Add unassign-from-user safe output handler implementation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.../setup/js/safe_output_handler_manager.cjs | 1 +
.../safe_output_unified_handler_manager.cjs | 1 +
actions/setup/js/safe_outputs_tools.json | 29 ++
actions/setup/js/unassign_from_user.cjs | 160 +++++++++++
actions/setup/js/unassign_from_user.test.cjs | 270 ++++++++++++++++++
pkg/cli/workflows/test-unassign-from-user.md | 40 +++
pkg/workflow/compiler_types.go | 3 +-
pkg/workflow/safe_outputs_config.go | 6 +
pkg/workflow/unassign_from_user.go | 44 +++
9 files changed, 553 insertions(+), 1 deletion(-)
create mode 100644 actions/setup/js/unassign_from_user.cjs
create mode 100644 actions/setup/js/unassign_from_user.test.cjs
create mode 100644 pkg/cli/workflows/test-unassign-from-user.md
create mode 100644 pkg/workflow/unassign_from_user.go
diff --git a/actions/setup/js/safe_output_handler_manager.cjs b/actions/setup/js/safe_output_handler_manager.cjs
index de615d7f8d..720b73f332 100644
--- a/actions/setup/js/safe_output_handler_manager.cjs
+++ b/actions/setup/js/safe_output_handler_manager.cjs
@@ -43,6 +43,7 @@ const HANDLER_MAP = {
add_reviewer: "./add_reviewer.cjs",
assign_milestone: "./assign_milestone.cjs",
assign_to_user: "./assign_to_user.cjs",
+ unassign_from_user: "./unassign_from_user.cjs",
create_code_scanning_alert: "./create_code_scanning_alert.cjs",
autofix_code_scanning_alert: "./autofix_code_scanning_alert.cjs",
dispatch_workflow: "./dispatch_workflow.cjs",
diff --git a/actions/setup/js/safe_output_unified_handler_manager.cjs b/actions/setup/js/safe_output_unified_handler_manager.cjs
index 1ac542fb41..cce0ea8934 100644
--- a/actions/setup/js/safe_output_unified_handler_manager.cjs
+++ b/actions/setup/js/safe_output_unified_handler_manager.cjs
@@ -51,6 +51,7 @@ const HANDLER_MAP = {
add_reviewer: "./add_reviewer.cjs",
assign_milestone: "./assign_milestone.cjs",
assign_to_user: "./assign_to_user.cjs",
+ unassign_from_user: "./unassign_from_user.cjs",
create_code_scanning_alert: "./create_code_scanning_alert.cjs",
autofix_code_scanning_alert: "./autofix_code_scanning_alert.cjs",
dispatch_workflow: "./dispatch_workflow.cjs",
diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json
index b396d04dde..9485637335 100644
--- a/actions/setup/js/safe_outputs_tools.json
+++ b/actions/setup/js/safe_outputs_tools.json
@@ -405,6 +405,35 @@
"additionalProperties": false
}
},
+ {
+ "name": "unassign_from_user",
+ "description": "Remove one or more assignees from an issue. Use this to unassign users when work is being reassigned or removed from their queue.",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "issue_number": {
+ "type": ["number", "string"],
+ "description": "Issue number to unassign users from. This is the numeric ID from the GitHub URL (e.g., 543 in github.com/owner/repo/issues/543). If omitted, uses the issue that triggered this workflow."
+ },
+ "assignees": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "GitHub usernames to unassign from the issue (e.g., ['octocat', 'mona'])."
+ },
+ "assignee": {
+ "type": "string",
+ "description": "Single GitHub username to unassign. Use 'assignees' array for multiple users."
+ },
+ "repo": {
+ "type": "string",
+ "description": "Target repository in 'owner/repo' format. If omitted, uses the current repository. Must be in allowed-repos list if specified."
+ }
+ },
+ "additionalProperties": false
+ }
+ },
{
"name": "update_issue",
"description": "Update an existing GitHub issue's status, title, or body. Use this to modify issue properties after creation. Only the fields you specify will be updated; other fields remain unchanged.",
diff --git a/actions/setup/js/unassign_from_user.cjs b/actions/setup/js/unassign_from_user.cjs
new file mode 100644
index 0000000000..676496b8f4
--- /dev/null
+++ b/actions/setup/js/unassign_from_user.cjs
@@ -0,0 +1,160 @@
+// @ts-check
+///
+
+/**
+ * @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction
+ */
+
+const { processItems } = require("./safe_output_processor.cjs");
+const { getErrorMessage } = require("./error_helpers.cjs");
+const { resolveTargetRepoConfig, resolveAndValidateRepo } = require("./repo_helpers.cjs");
+
+/** @type {string} Safe output type handled by this module */
+const HANDLER_TYPE = "unassign_from_user";
+
+/**
+ * Main handler factory for unassign_from_user
+ * Returns a message handler function that processes individual unassign_from_user messages
+ * @type {HandlerFactoryFunction}
+ */
+async function main(config = {}) {
+ // Extract configuration
+ const allowedAssignees = config.allowed || [];
+ const maxCount = config.max || 10;
+
+ // Resolve target repository configuration
+ const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config);
+
+ core.info(`Unassign from user configuration: max=${maxCount}`);
+ if (allowedAssignees.length > 0) {
+ core.info(`Allowed assignees to unassign: ${allowedAssignees.join(", ")}`);
+ }
+ core.info(`Default target repository: ${defaultTargetRepo}`);
+ if (allowedRepos.size > 0) {
+ core.info(`Additional allowed repositories: ${Array.from(allowedRepos).join(", ")}`);
+ }
+
+ // Track how many items we've processed for max limit
+ let processedCount = 0;
+
+ /**
+ * Message handler function that processes a single unassign_from_user message
+ * @param {Object} message - The unassign_from_user message to process
+ * @param {Object} resolvedTemporaryIds - Map of temporary IDs to {repo, number}
+ * @returns {Promise