From 791c88195f8c26190eb6d6ffc025ed7476819bc1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:37:00 +0000 Subject: [PATCH 1/7] Initial plan From cf6390eccb2a0515ed794e91aec2ff7c9a90d608 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:46:45 +0000 Subject: [PATCH 2/7] Refactor TOOLS JSON payload in safe outputs MCP server Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 45 +++- .github/workflows/audit-workflows.lock.yml | 46 +++- .github/workflows/brave.lock.yml | 45 +++- .../workflows/changeset-generator.lock.yml | 46 +++- .github/workflows/ci-doctor.lock.yml | 45 +++- .../workflows/cli-version-checker.lock.yml | 46 +++- .github/workflows/daily-news.lock.yml | 45 +++- .github/workflows/dev.lock.yml | 45 +++- .../duplicate-code-detector.lock.yml | 45 +++- .../workflows/go-pattern-detector.lock.yml | 46 +++- .github/workflows/issue-classifier.lock.yml | 45 +++- .../issue-summarizer-genaiscript.lock.yml | 45 +++- .../workflows/notion-issue-summary.lock.yml | 46 +++- .github/workflows/pdf-summary.lock.yml | 45 +++- .github/workflows/plan.lock.yml | 45 +++- .github/workflows/poem-bot.lock.yml | 45 +++- .github/workflows/repo-tree-map.lock.yml | 45 +++- .github/workflows/scout.lock.yml | 45 +++- .github/workflows/security-fix-pr.lock.yml | 46 +++- .github/workflows/smoke-claude.lock.yml | 46 +++- .github/workflows/smoke-codex.lock.yml | 45 +++- .github/workflows/smoke-copilot.lock.yml | 45 +++- .github/workflows/smoke-genaiscript.lock.yml | 45 +++- .../workflows/technical-doc-writer.lock.yml | 46 +++- .github/workflows/tidy.lock.yml | 45 +++- .github/workflows/unbloat-docs.lock.yml | 46 +++- pkg/workflow/claude_engine.go | 1 + pkg/workflow/data/safe_outputs_tools.json | 236 ++++++++++++++++++ pkg/workflow/js/safe_outputs_mcp_server.cjs | 57 ++++- pkg/workflow/mcps.go | 8 + pkg/workflow/safe_outputs_tools.go | 208 +++++++++++++++ 31 files changed, 1525 insertions(+), 164 deletions(-) create mode 100644 pkg/workflow/data/safe_outputs_tools.json create mode 100644 pkg/workflow/safe_outputs_tools.go diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 40f539533c2..a8114141fc6 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -459,6 +459,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -701,17 +714,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -875,6 +907,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_discussion\":{\"name\":\"create_discussion\",\"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\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 4833ebdee42..8d1e644758e 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -603,6 +603,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -845,17 +858,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1019,6 +1051,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_discussion\":{\"name\":\"create_discussion\",\"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\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1048,6 +1081,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index e5f0a5e0b14..b95df7cad6d 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -924,6 +924,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1166,17 +1179,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1340,6 +1372,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index a84b00cb558..d71cdf610df 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -894,6 +894,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1136,17 +1149,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1310,6 +1342,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}},\"push_to_pull_request_branch\":{\"name\":\"push_to_pull_request_branch\",\"description\":\"Push changes to a pull request branch\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"branch\":{\"description\":\"Optional branch name. 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\"},\"hasHandler\":true}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1335,6 +1368,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index a184a5fb815..1af918d42c9 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -430,6 +430,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -672,17 +685,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -846,6 +878,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-issue\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 601602e1ad0..df58a6f8c81 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -565,6 +565,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -807,17 +820,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -981,6 +1013,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_pull_request\":{\"name\":\"create_pull_request\",\"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\"},\"hasHandler\":true},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1006,6 +1039,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index b7145009fb5..226a791e0f1 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -461,6 +461,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -703,17 +716,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -877,6 +909,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_discussion\":{\"name\":\"create_discussion\",\"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\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 820845aad76..44bbb9364c1 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -609,6 +609,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -851,17 +864,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1025,6 +1057,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index a2d85c68e98..959fd7c0754 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -475,6 +475,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -717,17 +730,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -891,6 +923,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index d23e229e2bc..92ba938fbf7 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -572,6 +572,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -814,17 +827,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -988,6 +1020,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1023,6 +1056,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 745a52b33dc..6a409d563dc 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -782,6 +782,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1024,17 +1037,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1198,6 +1230,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\"],\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_labels\":{\"name\":\"add_labels\",\"description\":\"Add labels to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"issue_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\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' diff --git a/.github/workflows/issue-summarizer-genaiscript.lock.yml b/.github/workflows/issue-summarizer-genaiscript.lock.yml index 65fed08ae22..ad38e296f75 100644 --- a/.github/workflows/issue-summarizer-genaiscript.lock.yml +++ b/.github/workflows/issue-summarizer-genaiscript.lock.yml @@ -611,6 +611,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -853,17 +866,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1027,6 +1059,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 142304b26ef..a111ddc4158 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -727,6 +727,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -969,17 +982,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1143,6 +1175,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"notion-add-comment\":{\"description\":\"Add a comment to a Notion page\",\"inputs\":{\"comment\":{\"description\":\"The comment text to add\",\"required\":true,\"type\":\"string\"},\"page_id\":{\"description\":\"The Notion page ID to add a comment to\",\"required\":true,\"type\":\"string\"}},\"output\":\"Comment added to Notion successfully!\"}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1183,6 +1216,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 1139fcbc434..dd764e4cdce 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -838,6 +838,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1080,17 +1093,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1254,6 +1286,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index d600795423a..91d6d333175 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -793,6 +793,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1035,17 +1048,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1209,6 +1241,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":5},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 0898aba249d..dd86f3537f6 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -823,6 +823,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1065,17 +1078,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1239,6 +1271,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"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\":{},\"push-to-pull-request-branch\":{},\"update-issue\":{\"max\":2},\"upload-asset\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"add_labels\":{\"name\":\"add_labels\",\"description\":\"Add labels to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"issue_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\"}},\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"create_pull_request\":{\"name\":\"create_pull_request\",\"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\"},\"hasHandler\":true},\"create_pull_request_review_comment\":{\"name\":\"create_pull_request_review_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\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}},\"push_to_pull_request_branch\":{\"name\":\"push_to_pull_request_branch\",\"description\":\"Push changes to a pull request branch\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"branch\":{\"description\":\"Optional branch name. 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\"},\"hasHandler\":true},\"update_issue\":{\"name\":\"update_issue\",\"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\"}},\"upload_asset\":{\"name\":\"upload_asset\",\"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\"},\"hasHandler\":true}}" GITHUB_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}" GITHUB_AW_ASSETS_MAX_SIZE_KB: 10240 GITHUB_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg" diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 1754d20e006..b84ffd03ef6 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -457,6 +457,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -699,17 +712,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -873,6 +905,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_discussion\":{\"name\":\"create_discussion\",\"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\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 157d7d87c7c..9fa7e26b18a 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1104,6 +1104,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -1346,17 +1359,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1520,6 +1552,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index de0d3b497d6..59080a68181 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -563,6 +563,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -805,17 +818,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -979,6 +1011,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_pull_request\":{\"name\":\"create_pull_request\",\"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\"},\"hasHandler\":true},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1005,6 +1038,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index cf3c144f9bf..a68b2a56062 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -561,6 +561,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -803,17 +816,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -977,6 +1009,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1002,6 +1035,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 4fcf628a6c2..92833a28936 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -453,6 +453,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -695,17 +708,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -869,6 +901,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 25870ca5dcf..d60b2ea8605 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -455,6 +455,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -697,17 +710,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -871,6 +903,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index a623bfc01a3..8a554272fee 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -455,6 +455,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -697,17 +710,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -871,6 +903,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_issue\":{\"name\":\"create_issue\",\"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\"},\"title\":{\"description\":\"Issue title\",\"type\":\"string\"}},\"required\":[\"title\",\"body\"],\"type\":\"object\"}},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 94cfa7bdbc9..a56dbda1578 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -600,6 +600,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -842,17 +855,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1016,6 +1048,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-pull-request\":{},\"missing-tool\":{},\"upload-asset\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"create_pull_request\":{\"name\":\"create_pull_request\",\"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\"},\"hasHandler\":true},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}},\"upload_asset\":{\"name\":\"upload_asset\",\"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\"},\"hasHandler\":true}}" GITHUB_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}" GITHUB_AW_ASSETS_MAX_SIZE_KB: 10240 GITHUB_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg" @@ -1044,6 +1077,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 7c49c9b0e09..841bc088ad5 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -666,6 +666,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -908,17 +921,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1082,6 +1114,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"create_pull_request\":{\"name\":\"create_pull_request\",\"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\"},\"hasHandler\":true},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}},\"push_to_pull_request_branch\":{\"name\":\"push_to_pull_request_branch\",\"description\":\"Push changes to a pull request branch\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"branch\":{\"description\":\"Optional branch name. 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\"},\"hasHandler\":true}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 5e213fd3b24..a04fef1ff60 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -753,6 +753,19 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + let PREDEFINED_TOOLS = {}; + const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } + } const ALL_TOOLS = [ { name: "create_issue", @@ -995,17 +1008,36 @@ jobs: debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); + if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); + } else { + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); + } Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { name: normalizedKey, @@ -1169,6 +1201,7 @@ jobs: env: GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-pull-request\":{},\"missing-tool\":{}}" + GITHUB_AW_SAFE_OUTPUTS_TOOLS: "{\"add_comment\":{\"name\":\"add_comment\",\"description\":\"Add a comment to a GitHub issue or pull request\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"body\":{\"description\":\"Comment body/content\",\"type\":\"string\"},\"issue_number\":{\"description\":\"Issue or PR number (optional for current context)\",\"type\":\"number\"}},\"required\":[\"body\"],\"type\":\"object\"}},\"create_pull_request\":{\"name\":\"create_pull_request\",\"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\"},\"hasHandler\":true},\"missing_tool\":{\"name\":\"missing_tool\",\"description\":\"Report a missing tool or functionality needed to complete tasks\",\"inputSchema\":{\"additionalProperties\":false,\"properties\":{\"alternatives\":{\"description\":\"Possible alternatives or workarounds\",\"type\":\"string\"},\"reason\":{\"description\":\"Why this tool is needed\",\"type\":\"string\"},\"tool\":{\"description\":\"Name of the missing tool\",\"type\":\"string\"}},\"required\":[\"tool\",\"reason\"],\"type\":\"object\"}}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF' @@ -1194,6 +1227,7 @@ jobs: "env": { "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, + "GITHUB_AW_SAFE_OUTPUTS_TOOLS": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }}, "GITHUB_AW_ASSETS_BRANCH": "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 120281d2a5f..7aa3bf255ce 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -796,6 +796,7 @@ func (e *ClaudeEngine) renderSafeOutputsMCPConfig(yaml *strings.Builder, isLast yaml.WriteString(" \"env\": {\n") yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS\": \"${{ env.GITHUB_AW_SAFE_OUTPUTS }}\",\n") yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS_CONFIG\": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }},\n") + yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS_TOOLS\": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_TOOLS) }},\n") yaml.WriteString(" \"GITHUB_AW_ASSETS_BRANCH\": \"${{ env.GITHUB_AW_ASSETS_BRANCH }}\",\n") yaml.WriteString(" \"GITHUB_AW_ASSETS_MAX_SIZE_KB\": \"${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}\",\n") yaml.WriteString(" \"GITHUB_AW_ASSETS_ALLOWED_EXTS\": \"${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}\"\n") diff --git a/pkg/workflow/data/safe_outputs_tools.json b/pkg/workflow/data/safe_outputs_tools.json new file mode 100644 index 00000000000..30a39a08b1f --- /dev/null +++ b/pkg/workflow/data/safe_outputs_tools.json @@ -0,0 +1,236 @@ +[ + { + "name": "create_issue", + "description": "Create a new GitHub issue", + "inputSchema": { + "type": "object", + "required": ["title", "body"], + "properties": { + "title": { "type": "string", "description": "Issue title" }, + "body": { "type": "string", "description": "Issue body/description" }, + "labels": { + "type": "array", + "items": { "type": "string" }, + "description": "Issue labels" + } + }, + "additionalProperties": false + } + }, + { + "name": "create_discussion", + "description": "Create a new GitHub discussion", + "inputSchema": { + "type": "object", + "required": ["title", "body"], + "properties": { + "title": { "type": "string", "description": "Discussion title" }, + "body": { "type": "string", "description": "Discussion body/content" }, + "category": { "type": "string", "description": "Discussion category" } + }, + "additionalProperties": false + } + }, + { + "name": "add_comment", + "description": "Add a comment to a GitHub issue or pull request", + "inputSchema": { + "type": "object", + "required": ["body"], + "properties": { + "body": { "type": "string", "description": "Comment body/content" }, + "issue_number": { + "type": "number", + "description": "Issue or PR number (optional for current context)" + } + }, + "additionalProperties": false + } + }, + { + "name": "create_pull_request", + "description": "Create a new GitHub pull request", + "inputSchema": { + "type": "object", + "required": ["title", "body"], + "properties": { + "title": { "type": "string", "description": "Pull request title" }, + "body": { + "type": "string", + "description": "Pull request body/description" + }, + "branch": { + "type": "string", + "description": "Optional branch name. If not provided, the current branch will be used." + }, + "labels": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional labels to add to the PR" + } + }, + "additionalProperties": false + }, + "hasHandler": true + }, + { + "name": "create_pull_request_review_comment", + "description": "Create a review comment on a GitHub pull request", + "inputSchema": { + "type": "object", + "required": ["path", "line", "body"], + "properties": { + "path": { + "type": "string", + "description": "File path for the review comment" + }, + "line": { + "type": ["number", "string"], + "description": "Line number for the comment" + }, + "body": { "type": "string", "description": "Comment body content" }, + "start_line": { + "type": ["number", "string"], + "description": "Optional start line for multi-line comments" + }, + "side": { + "type": "string", + "enum": ["LEFT", "RIGHT"], + "description": "Optional side of the diff: LEFT or RIGHT" + } + }, + "additionalProperties": false + } + }, + { + "name": "create_code_scanning_alert", + "description": "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", + "inputSchema": { + "type": "object", + "required": ["file", "line", "severity", "message"], + "properties": { + "file": { + "type": "string", + "description": "File path where the issue was found" + }, + "line": { + "type": ["number", "string"], + "description": "Line number where the issue was found" + }, + "severity": { + "type": "string", + "enum": ["error", "warning", "info", "note"], + "description": " Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of \"error\", \"warning\", \"info\", \"note\"." + }, + "message": { + "type": "string", + "description": "Alert message describing the issue" + }, + "column": { + "type": ["number", "string"], + "description": "Optional column number" + }, + "ruleIdSuffix": { + "type": "string", + "description": "Optional rule ID suffix for uniqueness" + } + }, + "additionalProperties": false + } + }, + { + "name": "add_labels", + "description": "Add labels to a GitHub issue or pull request", + "inputSchema": { + "type": "object", + "required": ["labels"], + "properties": { + "labels": { + "type": "array", + "items": { "type": "string" }, + "description": "Labels to add" + }, + "issue_number": { + "type": "number", + "description": "Issue or PR number (optional for current context)" + } + }, + "additionalProperties": false + } + }, + { + "name": "update_issue", + "description": "Update a GitHub issue", + "inputSchema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["open", "closed"], + "description": "Optional new issue status" + }, + "title": { "type": "string", "description": "Optional new issue title" }, + "body": { "type": "string", "description": "Optional new issue body" }, + "issue_number": { + "type": ["number", "string"], + "description": "Optional issue number for target '*'" + } + }, + "additionalProperties": false + } + }, + { + "name": "push_to_pull_request_branch", + "description": "Push changes to a pull request branch", + "inputSchema": { + "type": "object", + "required": ["message"], + "properties": { + "branch": { + "type": "string", + "description": "Optional branch name. If not provided, the current branch will be used." + }, + "message": { "type": "string", "description": "Commit message" }, + "pull_request_number": { + "type": ["number", "string"], + "description": "Optional pull request number for target '*'" + } + }, + "additionalProperties": false + }, + "hasHandler": true + }, + { + "name": "upload_asset", + "description": "Publish a file as a URL-addressable asset to an orphaned git branch", + "inputSchema": { + "type": "object", + "required": ["path"], + "properties": { + "path": { + "type": "string", + "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." + } + }, + "additionalProperties": false + }, + "hasHandler": true + }, + { + "name": "missing_tool", + "description": "Report a missing tool or functionality needed to complete tasks", + "inputSchema": { + "type": "object", + "required": ["tool", "reason"], + "properties": { + "tool": { "type": "string", "description": "Name of the missing tool" }, + "reason": { "type": "string", "description": "Why this tool is needed" }, + "alternatives": { + "type": "string", + "description": "Possible alternatives or workarounds" + } + }, + "additionalProperties": false + } + } +] diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index 334bccd1efc..17638c4314b 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -332,6 +332,24 @@ const pushToPullRequestBranchHandler = args => { }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); + +// Load tools from environment variable or use legacy fallback +let PREDEFINED_TOOLS = {}; +const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; + +if (toolsEnv) { + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + debug(`Falling back to legacy ALL_TOOLS filtering`); + PREDEFINED_TOOLS = {}; + } +} + +// Legacy ALL_TOOLS array - kept for backward compatibility when env var not provided const ALL_TOOLS = [ { name: "create_issue", @@ -578,12 +596,34 @@ debug(` config: ${JSON.stringify(safeOutputsConfig)}`); // Create a comprehensive tools map including both predefined tools and dynamic safe-jobs const TOOLS = {}; -// Add predefined tools that are enabled in configuration -ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } -}); +// If we have predefined tools from environment, use them directly (pre-filtered by Go) +if (Object.keys(PREDEFINED_TOOLS).length > 0) { + debug(`Using pre-filtered tools from environment variable`); + // Add handlers for tools that need them + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + + // Attach handlers for specific tools + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; + } + } + }); +} else { + // Legacy fallback: filter ALL_TOOLS based on configuration + debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); + ALL_TOOLS.forEach(tool => { + if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { + TOOLS[tool.name] = tool; + } + }); +} // Add safe-jobs as dynamic tools Object.keys(safeOutputsConfig).forEach(configKey => { @@ -594,8 +634,9 @@ Object.keys(safeOutputsConfig).forEach(configKey => { return; } - // Check if this is a safe-job (not in ALL_TOOLS) - if (!ALL_TOOLS.find(t => t.name === normalizedKey)) { + // Check if this is a safe-job (not in ALL_TOOLS or PREDEFINED_TOOLS) + const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; // Create a dynamic tool for this safe-job diff --git a/pkg/workflow/mcps.go b/pkg/workflow/mcps.go index f8ecd7fac95..0df1f11b81e 100644 --- a/pkg/workflow/mcps.go +++ b/pkg/workflow/mcps.go @@ -163,10 +163,18 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, yaml.WriteString(" - name: Setup MCPs\n") if HasSafeOutputsEnabled(workflowData.SafeOutputs) { if safeOutputConfig != "" { + // Generate filtered tools JSON + filteredToolsJSON, err := c.GenerateFilteredToolsJSON(workflowData) + if err != nil { + // Log error but continue with empty tools + filteredToolsJSON = "{}" + } + // Add environment variables for JSONL validation yaml.WriteString(" env:\n") fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}\n") fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS_CONFIG: %q\n", safeOutputConfig) + fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS_TOOLS: %q\n", filteredToolsJSON) if workflowData.SafeOutputs != nil && workflowData.SafeOutputs.UploadAssets != nil { fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_BRANCH: %q\n", workflowData.SafeOutputs.UploadAssets.BranchName) fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_MAX_SIZE_KB: %d\n", workflowData.SafeOutputs.UploadAssets.MaxSizeKB) diff --git a/pkg/workflow/safe_outputs_tools.go b/pkg/workflow/safe_outputs_tools.go new file mode 100644 index 00000000000..4c1cfbfc4d0 --- /dev/null +++ b/pkg/workflow/safe_outputs_tools.go @@ -0,0 +1,208 @@ +package workflow + +import ( + _ "embed" + "encoding/json" +) + +//go:embed data/safe_outputs_tools.json +var safeOutputsToolsJSON string + +// SafeOutputToolDefinition represents a tool definition for the safe-outputs MCP server +type SafeOutputToolDefinition struct { + Name string `json:"name"` + Description string `json:"description"` + InputSchema map[string]interface{} `json:"inputSchema"` + HasHandler bool `json:"hasHandler,omitempty"` +} + +// GetSafeOutputsToolsDefinitions returns the parsed tool definitions +func GetSafeOutputsToolsDefinitions() ([]SafeOutputToolDefinition, error) { + var tools []SafeOutputToolDefinition + if err := json.Unmarshal([]byte(safeOutputsToolsJSON), &tools); err != nil { + return nil, err + } + return tools, nil +} + +// GenerateFilteredToolsJSON generates a JSON string of tools filtered by the safe-outputs configuration +// The output is a JSON object mapping normalized tool names to tool definitions +func (c *Compiler) GenerateFilteredToolsJSON(data *WorkflowData) (string, error) { + if data == nil || data.SafeOutputs == nil { + return "{}", nil + } + + // Get all tool definitions + allTools, err := GetSafeOutputsToolsDefinitions() + if err != nil { + return "", err + } + + // Build a map of enabled tools based on safe-outputs configuration + enabledTools := make(map[string]SafeOutputToolDefinition) + + // Helper function to normalize tool names (convert dashes to underscores, lowercase) + normalizeTool := func(name string) string { + // This matches the normTool function in the JavaScript + result := "" + for _, ch := range name { + if ch == '-' { + result += "_" + } else { + result += string(ch) + } + } + // JavaScript toLowerCase is locale-independent for ASCII + return result + } + + // Check which tools are enabled in the configuration + safeOutputsConfig := c.generateSafeOutputsConfigMap(data) + + for _, tool := range allTools { + // Check if this tool is enabled in the configuration + for configKey := range safeOutputsConfig { + if normalizeTool(configKey) == tool.Name { + enabledTools[tool.Name] = tool + break + } + } + } + + // Convert to JSON + toolsJSON, err := json.Marshal(enabledTools) + if err != nil { + return "", err + } + + return string(toolsJSON), nil +} + +// generateSafeOutputsConfigMap generates a map of safe-outputs configuration +// This is similar to generateSafeOutputsConfig but returns a map instead of JSON string +func (c *Compiler) generateSafeOutputsConfigMap(data *WorkflowData) map[string]interface{} { + safeOutputsConfig := make(map[string]interface{}) + + if data.SafeOutputs == nil { + return safeOutputsConfig + } + + // Handle safe-outputs configuration if present + if data.SafeOutputs.CreateIssues != nil { + issueConfig := map[string]interface{}{} + if data.SafeOutputs.CreateIssues.Max > 0 { + issueConfig["max"] = data.SafeOutputs.CreateIssues.Max + } + if data.SafeOutputs.CreateIssues.Min > 0 { + issueConfig["min"] = data.SafeOutputs.CreateIssues.Min + } + safeOutputsConfig["create-issue"] = issueConfig + } + if data.SafeOutputs.AddComments != nil { + commentConfig := map[string]interface{}{} + if data.SafeOutputs.AddComments.Target != "" { + commentConfig["target"] = data.SafeOutputs.AddComments.Target + } + if data.SafeOutputs.AddComments.Max > 0 { + commentConfig["max"] = data.SafeOutputs.AddComments.Max + } + if data.SafeOutputs.AddComments.Min > 0 { + commentConfig["min"] = data.SafeOutputs.AddComments.Min + } + safeOutputsConfig["add-comment"] = commentConfig + } + if data.SafeOutputs.CreateDiscussions != nil { + discussionConfig := map[string]interface{}{} + if data.SafeOutputs.CreateDiscussions.Max > 0 { + discussionConfig["max"] = data.SafeOutputs.CreateDiscussions.Max + } + if data.SafeOutputs.CreateDiscussions.Min > 0 { + discussionConfig["min"] = data.SafeOutputs.CreateDiscussions.Min + } + safeOutputsConfig["create-discussion"] = discussionConfig + } + if data.SafeOutputs.CreatePullRequests != nil { + prConfig := map[string]interface{}{} + if data.SafeOutputs.CreatePullRequests.Min > 0 { + prConfig["min"] = data.SafeOutputs.CreatePullRequests.Min + } + safeOutputsConfig["create-pull-request"] = prConfig + } + if data.SafeOutputs.CreatePullRequestReviewComments != nil { + prReviewCommentConfig := map[string]interface{}{} + if data.SafeOutputs.CreatePullRequestReviewComments.Max > 0 { + prReviewCommentConfig["max"] = data.SafeOutputs.CreatePullRequestReviewComments.Max + } + if data.SafeOutputs.CreatePullRequestReviewComments.Min > 0 { + prReviewCommentConfig["min"] = data.SafeOutputs.CreatePullRequestReviewComments.Min + } + safeOutputsConfig["create-pull-request-review-comment"] = prReviewCommentConfig + } + if data.SafeOutputs.CreateCodeScanningAlerts != nil { + securityReportConfig := map[string]interface{}{} + if data.SafeOutputs.CreateCodeScanningAlerts.Max > 0 { + securityReportConfig["max"] = data.SafeOutputs.CreateCodeScanningAlerts.Max + } + if data.SafeOutputs.CreateCodeScanningAlerts.Min > 0 { + securityReportConfig["min"] = data.SafeOutputs.CreateCodeScanningAlerts.Min + } + safeOutputsConfig["create-code-scanning-alert"] = securityReportConfig + } + if data.SafeOutputs.AddLabels != nil { + labelConfig := map[string]interface{}{} + if data.SafeOutputs.AddLabels.Max > 0 { + labelConfig["max"] = data.SafeOutputs.AddLabels.Max + } + if data.SafeOutputs.AddLabels.Min > 0 { + labelConfig["min"] = data.SafeOutputs.AddLabels.Min + } + if len(data.SafeOutputs.AddLabels.Allowed) > 0 { + labelConfig["allowed"] = data.SafeOutputs.AddLabels.Allowed + } + safeOutputsConfig["add-labels"] = labelConfig + } + if data.SafeOutputs.UpdateIssues != nil { + updateConfig := map[string]interface{}{} + if data.SafeOutputs.UpdateIssues.Max > 0 { + updateConfig["max"] = data.SafeOutputs.UpdateIssues.Max + } + if data.SafeOutputs.UpdateIssues.Min > 0 { + updateConfig["min"] = data.SafeOutputs.UpdateIssues.Min + } + safeOutputsConfig["update-issue"] = updateConfig + } + if data.SafeOutputs.PushToPullRequestBranch != nil { + pushToBranchConfig := map[string]interface{}{} + if data.SafeOutputs.PushToPullRequestBranch.Target != "" { + pushToBranchConfig["target"] = data.SafeOutputs.PushToPullRequestBranch.Target + } + if data.SafeOutputs.PushToPullRequestBranch.Max > 0 { + pushToBranchConfig["max"] = data.SafeOutputs.PushToPullRequestBranch.Max + } + if data.SafeOutputs.PushToPullRequestBranch.Min > 0 { + pushToBranchConfig["min"] = data.SafeOutputs.PushToPullRequestBranch.Min + } + safeOutputsConfig["push-to-pull-request-branch"] = pushToBranchConfig + } + if data.SafeOutputs.UploadAssets != nil { + uploadConfig := map[string]interface{}{} + if data.SafeOutputs.UploadAssets.Max > 0 { + uploadConfig["max"] = data.SafeOutputs.UploadAssets.Max + } + if data.SafeOutputs.UploadAssets.Min > 0 { + uploadConfig["min"] = data.SafeOutputs.UploadAssets.Min + } + safeOutputsConfig["upload-asset"] = uploadConfig + } + if data.SafeOutputs.MissingTool != nil { + missingToolConfig := map[string]interface{}{} + safeOutputsConfig["missing-tool"] = missingToolConfig + } + + // Add safe-jobs as well + for jobName := range data.SafeOutputs.Jobs { + safeOutputsConfig[jobName] = map[string]interface{}{} + } + + return safeOutputsConfig +} From 7f6e888acfbbaf52570916aa5f8ee3cf39d1aa17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:51:22 +0000 Subject: [PATCH 3/7] Apply formatting fixes Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/imports_test.go | 2 +- pkg/workflow/js/safe_outputs_mcp_server.cjs | 2 +- pkg/workflow/mcps.go | 2 +- pkg/workflow/safe_outputs_tools.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/cli/imports_test.go b/pkg/cli/imports_test.go index ce1c7585915..7a15cfddb96 100644 --- a/pkg/cli/imports_test.go +++ b/pkg/cli/imports_test.go @@ -217,7 +217,7 @@ func TestProcessIncludesWithWorkflowSpec_RealWorldScenario(t *testing.T) { // The workflow has: {{#import? agentics/weekly-research.config}} // Previously this would generate: githubnext/agentics/@e2770974... // Now it should generate: githubnext/agentics/agentics/weekly-research.config@e2770974... - + content := `--- on: schedule: diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index 17638c4314b..7bb206d34ad 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -603,7 +603,7 @@ if (Object.keys(PREDEFINED_TOOLS).length > 0) { Object.keys(PREDEFINED_TOOLS).forEach(toolName => { const tool = PREDEFINED_TOOLS[toolName]; TOOLS[toolName] = tool; - + // Attach handlers for specific tools if (tool.hasHandler) { if (toolName === "create_pull_request") { diff --git a/pkg/workflow/mcps.go b/pkg/workflow/mcps.go index 0df1f11b81e..f8f4b5594c3 100644 --- a/pkg/workflow/mcps.go +++ b/pkg/workflow/mcps.go @@ -169,7 +169,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, // Log error but continue with empty tools filteredToolsJSON = "{}" } - + // Add environment variables for JSONL validation yaml.WriteString(" env:\n") fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}\n") diff --git a/pkg/workflow/safe_outputs_tools.go b/pkg/workflow/safe_outputs_tools.go index 4c1cfbfc4d0..ceac1205589 100644 --- a/pkg/workflow/safe_outputs_tools.go +++ b/pkg/workflow/safe_outputs_tools.go @@ -58,7 +58,7 @@ func (c *Compiler) GenerateFilteredToolsJSON(data *WorkflowData) (string, error) // Check which tools are enabled in the configuration safeOutputsConfig := c.generateSafeOutputsConfigMap(data) - + for _, tool := range allTools { // Check if this tool is enabled in the configuration for configKey := range safeOutputsConfig { @@ -198,7 +198,7 @@ func (c *Compiler) generateSafeOutputsConfigMap(data *WorkflowData) map[string]i missingToolConfig := map[string]interface{}{} safeOutputsConfig["missing-tool"] = missingToolConfig } - + // Add safe-jobs as well for jobName := range data.SafeOutputs.Jobs { safeOutputsConfig[jobName] = map[string]interface{}{} From 0633328f526ac5f1becc16525075ff284a365f44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:55:09 +0000 Subject: [PATCH 4/7] Add tests for safe outputs tools filtering Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/safe_outputs_tools_test.go | 197 ++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 pkg/workflow/safe_outputs_tools_test.go diff --git a/pkg/workflow/safe_outputs_tools_test.go b/pkg/workflow/safe_outputs_tools_test.go new file mode 100644 index 00000000000..6c5b1a8bc63 --- /dev/null +++ b/pkg/workflow/safe_outputs_tools_test.go @@ -0,0 +1,197 @@ +package workflow + +import ( + "encoding/json" + "testing" +) + +func TestSafeOutputsToolsJSON(t *testing.T) { + // Test that we can parse the embedded tools JSON + tools, err := GetSafeOutputsToolsDefinitions() + if err != nil { + t.Fatalf("Failed to parse tools JSON: %v", err) + } + + // Verify we have the expected number of tools + expectedTools := []string{ + "create_issue", + "create_discussion", + "add_comment", + "create_pull_request", + "create_pull_request_review_comment", + "create_code_scanning_alert", + "add_labels", + "update_issue", + "push_to_pull_request_branch", + "upload_asset", + "missing_tool", + } + + if len(tools) != len(expectedTools) { + t.Errorf("Expected %d tools, got %d", len(expectedTools), len(tools)) + } + + // Verify each expected tool is present + toolMap := make(map[string]bool) + for _, tool := range tools { + toolMap[tool.Name] = true + } + + for _, expected := range expectedTools { + if !toolMap[expected] { + t.Errorf("Expected tool %q not found", expected) + } + } + + // Verify each tool has required fields + for _, tool := range tools { + if tool.Name == "" { + t.Errorf("Tool has empty name") + } + if tool.Description == "" { + t.Errorf("Tool %q has empty description", tool.Name) + } + if tool.InputSchema == nil { + t.Errorf("Tool %q has nil inputSchema", tool.Name) + } + } +} + +func TestGenerateFilteredToolsJSON(t *testing.T) { + tests := []struct { + name string + safeOutputs *SafeOutputsConfig + expectedTools []string + }{ + { + name: "No safe outputs", + safeOutputs: nil, + expectedTools: []string{}, + }, + { + name: "Single output - create-issue", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + MissingTool: &MissingToolConfig{}, + }, + expectedTools: []string{"create_issue", "missing_tool"}, + }, + { + name: "Multiple outputs", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + AddComments: &AddCommentsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + CreatePullRequests: &CreatePullRequestsConfig{}, + MissingTool: &MissingToolConfig{}, + }, + expectedTools: []string{"create_issue", "add_comment", "create_pull_request", "missing_tool"}, + }, + { + name: "Upload assets", + safeOutputs: &SafeOutputsConfig{ + UploadAssets: &UploadAssetsConfig{}, + MissingTool: &MissingToolConfig{}, + }, + expectedTools: []string{"upload_asset", "missing_tool"}, + }, + } + + compiler := NewCompiler(false, "", "test") + compiler.SetSkipValidation(true) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: tt.safeOutputs, + } + + toolsJSON, err := compiler.GenerateFilteredToolsJSON(data) + if err != nil { + t.Fatalf("Failed to generate filtered tools JSON: %v", err) + } + + // Parse the JSON + var tools map[string]SafeOutputToolDefinition + if err := json.Unmarshal([]byte(toolsJSON), &tools); err != nil { + t.Fatalf("Failed to parse tools JSON: %v", err) + } + + // Verify expected tools are present + if len(tools) != len(tt.expectedTools) { + t.Errorf("Expected %d tools, got %d", len(tt.expectedTools), len(tools)) + } + + for _, expected := range tt.expectedTools { + if _, ok := tools[expected]; !ok { + t.Errorf("Expected tool %q not found in filtered tools", expected) + } + } + + // Verify no unexpected tools + for toolName := range tools { + found := false + for _, expected := range tt.expectedTools { + if toolName == expected { + found = true + break + } + } + if !found { + t.Errorf("Unexpected tool %q found in filtered tools", toolName) + } + } + }) + } +} + +func TestGenerateFilteredToolsJSONWithHandlers(t *testing.T) { + compiler := NewCompiler(false, "", "test") + compiler.SetSkipValidation(true) + + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{}, + PushToPullRequestBranch: &PushToPullRequestBranchConfig{}, + UploadAssets: &UploadAssetsConfig{}, + MissingTool: &MissingToolConfig{}, + }, + } + + toolsJSON, err := compiler.GenerateFilteredToolsJSON(data) + if err != nil { + t.Fatalf("Failed to generate filtered tools JSON: %v", err) + } + + // Parse the JSON + var tools map[string]SafeOutputToolDefinition + if err := json.Unmarshal([]byte(toolsJSON), &tools); err != nil { + t.Fatalf("Failed to parse tools JSON: %v", err) + } + + // Verify hasHandler flag is set for tools that have handlers + toolsWithHandlers := []string{"create_pull_request", "push_to_pull_request_branch", "upload_asset"} + + for _, toolName := range toolsWithHandlers { + tool, ok := tools[toolName] + if !ok { + t.Errorf("Expected tool %q not found", toolName) + continue + } + if !tool.HasHandler { + t.Errorf("Tool %q should have hasHandler=true", toolName) + } + } + + // Verify missing_tool does not have hasHandler flag + if tool, ok := tools["missing_tool"]; ok { + if tool.HasHandler { + t.Errorf("Tool missing_tool should not have hasHandler flag") + } + } +} From 8dc873d8af3f9950c634db5f87dfb6b08f52f544 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:57:35 +0000 Subject: [PATCH 5/7] Complete refactor of TOOLS JSON payload in safe outputs MCP server Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/safe_outputs_tools_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/workflow/safe_outputs_tools_test.go b/pkg/workflow/safe_outputs_tools_test.go index 6c5b1a8bc63..2b72a626669 100644 --- a/pkg/workflow/safe_outputs_tools_test.go +++ b/pkg/workflow/safe_outputs_tools_test.go @@ -156,10 +156,10 @@ func TestGenerateFilteredToolsJSONWithHandlers(t *testing.T) { data := &WorkflowData{ SafeOutputs: &SafeOutputsConfig{ - CreatePullRequests: &CreatePullRequestsConfig{}, - PushToPullRequestBranch: &PushToPullRequestBranchConfig{}, - UploadAssets: &UploadAssetsConfig{}, - MissingTool: &MissingToolConfig{}, + CreatePullRequests: &CreatePullRequestsConfig{}, + PushToPullRequestBranch: &PushToPullRequestBranchConfig{}, + UploadAssets: &UploadAssetsConfig{}, + MissingTool: &MissingToolConfig{}, }, } @@ -176,7 +176,7 @@ func TestGenerateFilteredToolsJSONWithHandlers(t *testing.T) { // Verify hasHandler flag is set for tools that have handlers toolsWithHandlers := []string{"create_pull_request", "push_to_pull_request_branch", "upload_asset"} - + for _, toolName := range toolsWithHandlers { tool, ok := tools[toolName] if !ok { From 35b07da3515482d9412e60f43e0cb93ee0447e40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:08:24 +0000 Subject: [PATCH 6/7] Remove legacy support from safe-outputs MCP server Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 297 ++--------------- .github/workflows/audit-workflows.lock.yml | 297 ++--------------- .github/workflows/brave.lock.yml | 297 ++--------------- .../workflows/changeset-generator.lock.yml | 297 ++--------------- .github/workflows/ci-doctor.lock.yml | 297 ++--------------- .../workflows/cli-version-checker.lock.yml | 297 ++--------------- .github/workflows/daily-news.lock.yml | 297 ++--------------- .github/workflows/dev.lock.yml | 297 ++--------------- .../duplicate-code-detector.lock.yml | 297 ++--------------- .../workflows/go-pattern-detector.lock.yml | 297 ++--------------- .github/workflows/issue-classifier.lock.yml | 297 ++--------------- .../issue-summarizer-genaiscript.lock.yml | 297 ++--------------- .../workflows/notion-issue-summary.lock.yml | 297 ++--------------- .github/workflows/pdf-summary.lock.yml | 297 ++--------------- .github/workflows/plan.lock.yml | 297 ++--------------- .github/workflows/poem-bot.lock.yml | 297 ++--------------- .github/workflows/repo-tree-map.lock.yml | 297 ++--------------- .github/workflows/scout.lock.yml | 297 ++--------------- .github/workflows/security-fix-pr.lock.yml | 297 ++--------------- .github/workflows/smoke-claude.lock.yml | 297 ++--------------- .github/workflows/smoke-codex.lock.yml | 297 ++--------------- .github/workflows/smoke-copilot.lock.yml | 297 ++--------------- .github/workflows/smoke-genaiscript.lock.yml | 297 ++--------------- .../workflows/technical-doc-writer.lock.yml | 297 ++--------------- .github/workflows/tidy.lock.yml | 297 ++--------------- .github/workflows/unbloat-docs.lock.yml | 297 ++--------------- pkg/workflow/js/safe_outputs_mcp_server.cjs | 310 ++---------------- pkg/workflow/safe_outputs_mcp_server_test.go | 35 ++ 28 files changed, 715 insertions(+), 7352 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index a8114141fc6..15c5f21ab9e 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -459,290 +459,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 8d1e644758e..cb925f0e739 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -603,290 +603,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index b95df7cad6d..04b1f6d286e 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -924,290 +924,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index d71cdf610df..9101b234569 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -894,290 +894,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 1af918d42c9..abd99717d14 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -430,290 +430,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index df58a6f8c81..1a3a4b5f1c6 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -565,290 +565,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 226a791e0f1..476d895bdae 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -461,290 +461,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 44bbb9364c1..4ac5d1132fc 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -609,290 +609,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 959fd7c0754..0880c232a22 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -475,290 +475,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 92ba938fbf7..503b4f9edda 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -572,290 +572,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 6a409d563dc..23571a43f27 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -782,290 +782,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/issue-summarizer-genaiscript.lock.yml b/.github/workflows/issue-summarizer-genaiscript.lock.yml index ad38e296f75..f448881fa92 100644 --- a/.github/workflows/issue-summarizer-genaiscript.lock.yml +++ b/.github/workflows/issue-summarizer-genaiscript.lock.yml @@ -611,290 +611,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index a111ddc4158..38f56095185 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -727,290 +727,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index dd764e4cdce..b11a5d356f4 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -838,290 +838,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 91d6d333175..90a25bd877d 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -793,290 +793,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index dd86f3537f6..124cf1f210e 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -823,290 +823,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index b84ffd03ef6..dd0652df1fb 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -457,290 +457,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 9fa7e26b18a..f5cbc7725d1 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1104,290 +1104,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 59080a68181..5cb34830c1a 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -563,290 +563,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index a68b2a56062..2281920e5bc 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -561,290 +561,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 92833a28936..538f4d3d6aa 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -453,290 +453,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index d60b2ea8605..92eafdc6971 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -455,290 +455,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index 8a554272fee..50bfebad321 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -455,290 +455,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index a56dbda1578..67aad3913d6 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -600,290 +600,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 841bc088ad5..09dc3e33299 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -666,290 +666,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index a04fef1ff60..653a8b42d54 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -753,290 +753,43 @@ jobs: }; }; const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); - let PREDEFINED_TOOLS = {}; const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; - if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } + if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); + } + let PREDEFINED_TOOLS = {}; + debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); + try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); + } catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); } - const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, - ]; debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); debug(` config: ${JSON.stringify(safeOutputsConfig)}`); const TOOLS = {}; - if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } - } - }); - } else { - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; + debug(`Using pre-filtered tools from environment variable`); + Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); - } + } + }); Object.keys(safeOutputsConfig).forEach(configKey => { const normalizedKey = normTool(configKey); if (TOOLS[normalizedKey]) { return; } - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; const dynamicTool = { diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index 7bb206d34ad..922c6159be9 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -333,261 +333,22 @@ const pushToPullRequestBranchHandler = args => { const normTool = toolName => (toolName ? toolName.replace(/-/g, "_").toLowerCase() : undefined); -// Load tools from environment variable or use legacy fallback -let PREDEFINED_TOOLS = {}; +// Load tools from environment variable const toolsEnv = process.env.GITHUB_AW_SAFE_OUTPUTS_TOOLS; -if (toolsEnv) { - debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); - try { - PREDEFINED_TOOLS = JSON.parse(toolsEnv); - debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); - } catch (error) { - debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); - debug(`Falling back to legacy ALL_TOOLS filtering`); - PREDEFINED_TOOLS = {}; - } +if (!toolsEnv) { + throw new Error("GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable is required"); } -// Legacy ALL_TOOLS array - kept for backward compatibility when env var not provided -const ALL_TOOLS = [ - { - name: "create_issue", - description: "Create a new GitHub issue", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Issue title" }, - body: { type: "string", description: "Issue body/description" }, - labels: { - type: "array", - items: { type: "string" }, - description: "Issue labels", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_discussion", - description: "Create a new GitHub discussion", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Discussion title" }, - body: { type: "string", description: "Discussion body/content" }, - category: { type: "string", description: "Discussion category" }, - }, - additionalProperties: false, - }, - }, - { - name: "add_comment", - description: "Add a comment to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["body"], - properties: { - body: { type: "string", description: "Comment body/content" }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_pull_request", - description: "Create a new GitHub pull request", - inputSchema: { - type: "object", - required: ["title", "body"], - properties: { - title: { type: "string", description: "Pull request title" }, - body: { - type: "string", - description: "Pull request body/description", - }, - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - labels: { - type: "array", - items: { type: "string" }, - description: "Optional labels to add to the PR", - }, - }, - additionalProperties: false, - }, - handler: createPullRequestHandler, - }, - { - name: "create_pull_request_review_comment", - description: "Create a review comment on a GitHub pull request", - inputSchema: { - type: "object", - required: ["path", "line", "body"], - properties: { - path: { - type: "string", - description: "File path for the review comment", - }, - line: { - type: ["number", "string"], - description: "Line number for the comment", - }, - body: { type: "string", description: "Comment body content" }, - start_line: { - type: ["number", "string"], - description: "Optional start line for multi-line comments", - }, - side: { - type: "string", - enum: ["LEFT", "RIGHT"], - description: "Optional side of the diff: LEFT or RIGHT", - }, - }, - additionalProperties: false, - }, - }, - { - name: "create_code_scanning_alert", - description: "Create a code scanning alert. severity MUST be one of 'error', 'warning', 'info', 'note'.", - inputSchema: { - type: "object", - required: ["file", "line", "severity", "message"], - properties: { - file: { - type: "string", - description: "File path where the issue was found", - }, - line: { - type: ["number", "string"], - description: "Line number where the issue was found", - }, - severity: { - type: "string", - enum: ["error", "warning", "info", "note"], - description: - ' Security severity levels follow the industry-standard Common Vulnerability Scoring System (CVSS) that is also used for advisories in the GitHub Advisory Database and must be one of "error", "warning", "info", "note".', - }, - message: { - type: "string", - description: "Alert message describing the issue", - }, - column: { - type: ["number", "string"], - description: "Optional column number", - }, - ruleIdSuffix: { - type: "string", - description: "Optional rule ID suffix for uniqueness", - }, - }, - additionalProperties: false, - }, - }, - { - name: "add_labels", - description: "Add labels to a GitHub issue or pull request", - inputSchema: { - type: "object", - required: ["labels"], - properties: { - labels: { - type: "array", - items: { type: "string" }, - description: "Labels to add", - }, - issue_number: { - type: "number", - description: "Issue or PR number (optional for current context)", - }, - }, - additionalProperties: false, - }, - }, - { - name: "update_issue", - description: "Update a GitHub issue", - inputSchema: { - type: "object", - properties: { - status: { - type: "string", - enum: ["open", "closed"], - description: "Optional new issue status", - }, - title: { type: "string", description: "Optional new issue title" }, - body: { type: "string", description: "Optional new issue body" }, - issue_number: { - type: ["number", "string"], - description: "Optional issue number for target '*'", - }, - }, - additionalProperties: false, - }, - }, - { - name: "push_to_pull_request_branch", - description: "Push changes to a pull request branch", - inputSchema: { - type: "object", - required: ["message"], - properties: { - branch: { - type: "string", - description: "Optional branch name. If not provided, the current branch will be used.", - }, - message: { type: "string", description: "Commit message" }, - pull_request_number: { - type: ["number", "string"], - description: "Optional pull request number for target '*'", - }, - }, - additionalProperties: false, - }, - handler: pushToPullRequestBranchHandler, - }, - { - name: "upload_asset", - description: "Publish a file as a URL-addressable asset to an orphaned git branch", - inputSchema: { - type: "object", - required: ["path"], - properties: { - path: { - type: "string", - 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.", - }, - }, - additionalProperties: false, - }, - handler: uploadAssetHandler, - }, - { - name: "missing_tool", - description: "Report a missing tool or functionality needed to complete tasks", - inputSchema: { - type: "object", - required: ["tool", "reason"], - properties: { - tool: { type: "string", description: "Name of the missing tool" }, - reason: { type: "string", description: "Why this tool is needed" }, - alternatives: { - type: "string", - description: "Possible alternatives or workarounds", - }, - }, - additionalProperties: false, - }, - }, -]; +let PREDEFINED_TOOLS = {}; +debug(`Loading tools from GITHUB_AW_SAFE_OUTPUTS_TOOLS environment variable`); +try { + PREDEFINED_TOOLS = JSON.parse(toolsEnv); + debug(`Loaded ${Object.keys(PREDEFINED_TOOLS).length} predefined tools from environment`); +} catch (error) { + debug(`Error parsing GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to parse GITHUB_AW_SAFE_OUTPUTS_TOOLS: ${error instanceof Error ? error.message : String(error)}`); +} debug(`v${SERVER_INFO.version} ready on stdio`); debug(` output file: ${outputFile}`); @@ -596,34 +357,23 @@ debug(` config: ${JSON.stringify(safeOutputsConfig)}`); // Create a comprehensive tools map including both predefined tools and dynamic safe-jobs const TOOLS = {}; -// If we have predefined tools from environment, use them directly (pre-filtered by Go) -if (Object.keys(PREDEFINED_TOOLS).length > 0) { - debug(`Using pre-filtered tools from environment variable`); - // Add handlers for tools that need them - Object.keys(PREDEFINED_TOOLS).forEach(toolName => { - const tool = PREDEFINED_TOOLS[toolName]; - TOOLS[toolName] = tool; - - // Attach handlers for specific tools - if (tool.hasHandler) { - if (toolName === "create_pull_request") { - TOOLS[toolName].handler = createPullRequestHandler; - } else if (toolName === "push_to_pull_request_branch") { - TOOLS[toolName].handler = pushToPullRequestBranchHandler; - } else if (toolName === "upload_asset") { - TOOLS[toolName].handler = uploadAssetHandler; - } +// Use pre-filtered tools from environment (provided by Go) +debug(`Using pre-filtered tools from environment variable`); +Object.keys(PREDEFINED_TOOLS).forEach(toolName => { + const tool = PREDEFINED_TOOLS[toolName]; + TOOLS[toolName] = tool; + + // Attach handlers for specific tools + if (tool.hasHandler) { + if (toolName === "create_pull_request") { + TOOLS[toolName].handler = createPullRequestHandler; + } else if (toolName === "push_to_pull_request_branch") { + TOOLS[toolName].handler = pushToPullRequestBranchHandler; + } else if (toolName === "upload_asset") { + TOOLS[toolName].handler = uploadAssetHandler; } - }); -} else { - // Legacy fallback: filter ALL_TOOLS based on configuration - debug(`Using legacy ALL_TOOLS filtering (no GITHUB_AW_SAFE_OUTPUTS_TOOLS provided)`); - ALL_TOOLS.forEach(tool => { - if (Object.keys(safeOutputsConfig).find(config => normTool(config) === tool.name)) { - TOOLS[tool.name] = tool; - } - }); -} + } +}); // Add safe-jobs as dynamic tools Object.keys(safeOutputsConfig).forEach(configKey => { @@ -634,8 +384,8 @@ Object.keys(safeOutputsConfig).forEach(configKey => { return; } - // Check if this is a safe-job (not in ALL_TOOLS or PREDEFINED_TOOLS) - const isKnownTool = ALL_TOOLS.find(t => t.name === normalizedKey) || PREDEFINED_TOOLS[normalizedKey]; + // Check if this is a safe-job (not in PREDEFINED_TOOLS) + const isKnownTool = PREDEFINED_TOOLS[normalizedKey]; if (!isKnownTool) { const jobConfig = safeOutputsConfig[configKey]; diff --git a/pkg/workflow/safe_outputs_mcp_server_test.go b/pkg/workflow/safe_outputs_mcp_server_test.go index 2758a413748..9655ce5fd22 100644 --- a/pkg/workflow/safe_outputs_mcp_server_test.go +++ b/pkg/workflow/safe_outputs_mcp_server_test.go @@ -43,6 +43,41 @@ func NewMCPTestClient(t *testing.T, outputFile string, config map[string]any) *M t.Fatalf("Failed to marshal config: %v", err) } env = append(env, fmt.Sprintf("GITHUB_AW_SAFE_OUTPUTS_CONFIG=%s", string(configJSON))) + + // Generate filtered tools JSON based on the config + compiler := NewCompiler(false, "", "test") + compiler.SetSkipValidation(true) + + // Build a minimal WorkflowData with the safe outputs config + safeOutputs := &SafeOutputsConfig{} + for key := range config { + // Normalize the key to handle both dash and underscore formats + normalizedKey := strings.ReplaceAll(key, "_", "-") + switch normalizedKey { + case "create-issue": + safeOutputs.CreateIssues = &CreateIssuesConfig{} + case "create-discussion": + safeOutputs.CreateDiscussions = &CreateDiscussionsConfig{} + case "add-comment": + safeOutputs.AddComments = &AddCommentsConfig{} + case "create-pull-request": + safeOutputs.CreatePullRequests = &CreatePullRequestsConfig{} + case "upload-asset": + safeOutputs.UploadAssets = &UploadAssetsConfig{} + case "missing-tool": + safeOutputs.MissingTool = &MissingToolConfig{} + } + } + + workflowData := &WorkflowData{ + SafeOutputs: safeOutputs, + } + + toolsJSON, err := compiler.GenerateFilteredToolsJSON(workflowData) + if err != nil { + t.Fatalf("Failed to generate tools JSON: %v", err) + } + env = append(env, fmt.Sprintf("GITHUB_AW_SAFE_OUTPUTS_TOOLS=%s", toolsJSON)) } // Create command for the MCP server From c43d86f3b15f40de165789ebe95d305bf2d88685 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:32:28 +0000 Subject: [PATCH 7/7] Add logging to display filtered tools in MCP server Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 1 + .github/workflows/audit-workflows.lock.yml | 1 + .github/workflows/brave.lock.yml | 1 + .github/workflows/changeset-generator.lock.yml | 1 + .github/workflows/ci-doctor.lock.yml | 1 + .github/workflows/cli-version-checker.lock.yml | 1 + .github/workflows/daily-news.lock.yml | 1 + .github/workflows/dev.lock.yml | 1 + .github/workflows/duplicate-code-detector.lock.yml | 1 + .github/workflows/go-pattern-detector.lock.yml | 1 + .github/workflows/issue-classifier.lock.yml | 1 + .github/workflows/issue-summarizer-genaiscript.lock.yml | 1 + .github/workflows/notion-issue-summary.lock.yml | 1 + .github/workflows/pdf-summary.lock.yml | 1 + .github/workflows/plan.lock.yml | 1 + .github/workflows/poem-bot.lock.yml | 1 + .github/workflows/repo-tree-map.lock.yml | 1 + .github/workflows/scout.lock.yml | 1 + .github/workflows/security-fix-pr.lock.yml | 1 + .github/workflows/smoke-claude.lock.yml | 1 + .github/workflows/smoke-codex.lock.yml | 1 + .github/workflows/smoke-copilot.lock.yml | 1 + .github/workflows/smoke-genaiscript.lock.yml | 1 + .github/workflows/technical-doc-writer.lock.yml | 1 + .github/workflows/tidy.lock.yml | 1 + .github/workflows/unbloat-docs.lock.yml | 1 + pkg/workflow/js/safe_outputs_mcp_server.cjs | 3 +++ 27 files changed, 29 insertions(+) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 15c5f21ab9e..0d052d95c3e 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -550,6 +550,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index cb925f0e739..d26979bab35 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -694,6 +694,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 04b1f6d286e..34e3a0dc087 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1015,6 +1015,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index 9101b234569..d31c4725c5c 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -985,6 +985,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index abd99717d14..407462f3224 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -521,6 +521,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 1a3a4b5f1c6..0845b93abd3 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -656,6 +656,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 476d895bdae..35142718261 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -552,6 +552,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 4ac5d1132fc..cca86b54df3 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -700,6 +700,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 0880c232a22..4a77e5e10ee 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -566,6 +566,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 503b4f9edda..24f2e63d1df 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -663,6 +663,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 23571a43f27..d50712138ea 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -873,6 +873,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/issue-summarizer-genaiscript.lock.yml b/.github/workflows/issue-summarizer-genaiscript.lock.yml index f448881fa92..9893ad72d69 100644 --- a/.github/workflows/issue-summarizer-genaiscript.lock.yml +++ b/.github/workflows/issue-summarizer-genaiscript.lock.yml @@ -702,6 +702,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 38f56095185..a002467fd3c 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -818,6 +818,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index b11a5d356f4..0f1071003ac 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -929,6 +929,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 90a25bd877d..d46728a3894 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -884,6 +884,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 124cf1f210e..4585c18d018 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -914,6 +914,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index dd0652df1fb..d63948b2a3f 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -548,6 +548,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index f5cbc7725d1..84286a3c394 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1195,6 +1195,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 5cb34830c1a..6c6d5f45b07 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -654,6 +654,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 2281920e5bc..9d855b7e27b 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -652,6 +652,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 538f4d3d6aa..f24cf1861b0 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -544,6 +544,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 92eafdc6971..ab5eda014e3 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -546,6 +546,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index 50bfebad321..01eceff591d 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -546,6 +546,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 67aad3913d6..dbc1b22d062 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -691,6 +691,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 09dc3e33299..26626d910bc 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -757,6 +757,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 653a8b42d54..57e7c215091 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -844,6 +844,7 @@ jobs: }); debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); + console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); function handleMessage(req) { if (!req || typeof req !== "object") { debug(`Invalid message: not an object`); diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index 922c6159be9..ac4efaa8c13 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -457,6 +457,9 @@ Object.keys(safeOutputsConfig).forEach(configKey => { debug(` tools: ${Object.keys(TOOLS).join(", ")}`); if (!Object.keys(TOOLS).length) throw new Error("No tools enabled in configuration"); +// Log the filtered tools for debugging +console.error(`[${SERVER_INFO.name}] Filtered tools:\n${JSON.stringify(TOOLS, null, 2)}`); + function handleMessage(req) { // Validate basic JSON-RPC structure if (!req || typeof req !== "object") {