Skip to content

Add support for defined custom safe-output scripts#21582

Merged
pelikhan merged 12 commits intomainfrom
copilot/add-support-defined-custom-safe-output
Mar 19, 2026
Merged

Add support for defined custom safe-output scripts#21582
pelikhan merged 12 commits intomainfrom
copilot/add-support-defined-custom-safe-output

Conversation

Copy link
Contributor

Copilot AI commented Mar 18, 2026

Adds safe-outputs.scripts support — inline JavaScript handlers that run within the consolidated safe-outputs job's handler loop, similar to safe-outputs.jobs but without creating a separate GitHub Actions job.

Usage

Users write only the handler body — the code that executes inside the handler function for each item. The compiler automatically generates the full outer wrapper including config input destructuring and the handler function:

safe-outputs:
  scripts:
    slack-post-message:
      description: Post a message to Slack
      inputs:
        channel:
          description: Target channel
          required: true
          type: string
        message:
          description: Message text
          required: true
          type: string
      script: |
        const targetChannel = item.channel || "#general";
        const text = item.message || "(no message)";
        core.info(`[SLACK] → ${targetChannel}: ${text}`);
        return { success: true };

The compiler generates the full module file, including config destructuring, handler function wrapper, // @ts-check and a @type annotation for IDE support:

// @ts-check
/// <reference types="./safe-output-script" />
// Auto-generated safe-output script handler: slack-post-message

/** @type {import('./types/safe-output-script').SafeOutputScriptMain} */
async function main(config = {}) {
  const { channel, message } = config;  // auto-generated from declared inputs
  return async function handleSlackPostMessage(item, resolvedTemporaryIds, temporaryIdMap) {
    // ← user handler body here
    const targetChannel = item.channel || "#general";
    const text = item.message || "(no message)";
    core.info(`[SLACK] → ${targetChannel}: ${text}`);
    return { success: true };
  };
}
module.exports = { main };

Note: Per-call input values sent by the agent are available as direct top-level properties on the item message object (e.g. item.channel). Config-destructured variables (e.g. channel, message) provide access to static YAML config values such as defaults.

How It Works

  1. The compiler writes each inline script to ${RUNNER_TEMP}/gh-aw/actions/safe_output_script_<name>.cjs via a heredoc step in the safe-outputs job. The user's handler body is automatically wrapped with config destructuring and the handler function — users write only the handler body.
  2. GH_AW_SAFE_OUTPUT_SCRIPTS (a JSON map of normalized name → filename) is passed as an env var to the handler manager step.
  3. The handler manager loads each script, calls main(config), and dispatches messages of the matching type to the returned handler function.
  4. MCP tool definitions (name, description, inputSchema) are generated the same way as custom safe-jobs.

Changes Made

  • compiler_types.go — Added Scripts map[string]*SafeScriptConfig field to SafeOutputsConfig
  • safe_scripts.go — New file: SafeScriptConfig struct, parsing helpers, JSON env var builder, and generateCustomScriptToolDefinition()
  • safe_outputs_config.go — Parse scripts: key from frontmatter
  • safe_outputs_state.go — Include Scripts in hasAnySafeOutputEnabled() and hasNonBuiltinSafeOutputsEnabled()
  • safe_outputs_config_generation.go — Emit script config into GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG
  • safe_outputs_tools_filtering.go — Include script tool definitions in filtered and dynamic tools JSON
  • compiler_safe_outputs_job.gogenerateSafeOutputScriptContent() takes *SafeScriptConfig and generates the full outer wrapper: config input destructuring (const { channel, message } = config;), handler function (return async function handleX(item, resolvedTemporaryIds, temporaryIdMap) { ... }), and module.exports; added scriptNameToHandlerName() helper; buildCustomScriptFilesStep() writes wrapped scripts via heredoc
  • compiler_safe_outputs_steps.go — Emit GH_AW_SAFE_OUTPUT_SCRIPTS env var on handler manager step
  • safe_output_helpers.cjsloadCustomSafeOutputScriptHandlers() parses GH_AW_SAFE_OUTPUT_SCRIPTS
  • safe_output_handler_manager.cjs — Loads and dispatches to custom script handlers after built-in HANDLER_MAP handlers; custom script loading errors are non-fatal (emit core.warning and skip the handler rather than crashing the entire loop)
  • main_workflow_schema.json — Added scripts as a valid safe-outputs property with full sub-schema for inputs and script fields; updated script description to clarify users write the handler body
  • smoke-claude.md — Added post-slack-message script as a real-world usage example (handler body only), with a corresponding test step (Fix orphan removal on "gh aw remove" #11) that exercises the post_slack_message tool
  • actions/setup/js/types/safe-output-script.d.ts — TypeScript definition file documenting the types available when writing a custom safe-output script; includes ResolvedTemporaryIdEntry, TemporaryIdMap, updated SafeOutputScriptHandler with temporaryIdMap as 3rd parameter, and export declare function main

TypeScript Definitions

The safe-output-script.d.ts file exports the following types for script authors:

Type Description
SafeOutputScriptInputDefinition Schema for a single declared input (type, description, required, default, options)
SafeOutputScriptConfig The config parameter of main() — static YAML metadata (description + inputs schema)
SafeOutputScriptItem<TInputs> Per-call message passed to the handler — user input values as direct top-level properties
ResolvedTemporaryIdEntry A single resolved temporary ID entry (repo + number)
ResolvedTemporaryIds Plain-object snapshot of resolved temporary IDs (2nd handler argument)
TemporaryIdMap Live Map<string, ResolvedTemporaryIdEntry> (3rd handler argument)
SafeOutputScriptHandler<TInputs> Async handler function (item, resolvedTemporaryIds, temporaryIdMap) returned by main()
SafeOutputScriptMain<TInputs> Factory function type generated by the compiler
main (declared) Exported function declaration matching module.exports = { main } for TypeScript tooling validation

The generated .cjs files include // @ts-check and a @type JSDoc annotation so IDEs can type-check the generated main function.

Temporary ID Support

Custom script handlers receive the same three-argument calling convention as built-in handlers:

  • item — the per-call message with runtime input values
  • resolvedTemporaryIds — plain-object snapshot of resolved temporary IDs at invocation time
  • temporaryIdMap — live Map updated as new issues/PRs are created during the run

Error Handling

Custom script loading is fully non-fatal: syntax errors, missing main exports, or main() not returning a function all emit core.warning and skip that handler rather than crashing the entire safe-output loop. Errors during handler invocation are caught by the existing message processing loop and recorded as failed results without blocking other operations.

Testing

  • ✅ 19 new unit tests covering parsing, JSON generation, tool definition generation, script content wrapping (config destructuring + handler wrapper + // @ts-check), handler name generation, step generation, and handler manager integration
  • make build, make fmt, make lint all pass
  • ✅ 174 existing workflows recompile cleanly with no output changes
  • smoke-claude workflow recompiles cleanly with the new post-slack-message script
Original prompt

Add support for defined custom safe-output as scripts, similar safe output custom jobs.

safe-outputs:
scripts:
slack-post-message:
... inputs same as jobs
script: |
... # JavaScript handler

The script get written in the folder that holds the actions script and get loaded at runtime in the safe output handler message loop. The script should have the same JavaScript structure as existing handlers.

The compilation into the config and tools.json should reuse helped used by safe jobs.

The goal of this feature is to create safe output handlers that runs in the safe output loop and have better support for temporary ids.


📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.



✨ PR Review Safe Output Test - Run 23272536740

💥 [THE END] — Illustrated by Smoke Claude ·

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions

This comment has been minimized.

@github-actions
Copy link
Contributor

Hey @Copilot 👋 — great start on adding custom safe-output script support! The new safe_scripts.go and safe_scripts_test.go files lay a solid foundation for the feature, and the 377-line test file shows real coverage of parsing and config generation. A few things still need attention before this is ready for review:

  • WIP checklist — All checklist items in the PR body are still unchecked, including the final validation step. Work through each item and check them off as they are completed.
  • Run make agent-finish — Per the project's contribution process, make agent-finish (build + test + recompile + format + lint) must be run and pass before the PR is considered complete. This is a required quality gate per CONTRIBUTING.md and DEVGUIDE.md.
  • Draft status — Once all checklist items are done and make agent-finish passes, convert this from a draft to a ready-for-review PR so @pelikhan can do a final pass.

If you'd like a hand finishing the remaining items, you can use this prompt with your coding agent:

Continue implementing the WIP PR for "Add support for defined custom safe-output for scripts" in github/gh-aw...

Generated by Contribution Check ·

@pelikhan
Copy link
Contributor

@copilot keep working

Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot keep working

All checklist items are complete. The implementation adds safe-outputs.scripts support — inline JavaScript handlers that run within the consolidated safe-outputs job's handler loop (in contrast to safe-outputs.jobs which create separate GitHub Actions jobs).

Usage example:

safe-outputs:
  scripts:
    slack-post-message:
      description: Post a message to Slack
      inputs:
        channel:
          description: Target channel
          required: true
          type: string
        message:
          description: Message text
          required: true
          type: string
      script: |
        async function main(config = {}) {
          const { channel, message } = config;
          return async function handleSlackPostMessage(item) {
            // ... handler logic using github, context, core globals
            return { success: true };
          };
        }
        module.exports = { main };

How it works:

  1. Compiler writes the inline script to ${RUNNER_TEMP}/gh-aw/actions/safe_output_script_<name>.cjs via a heredoc step
  2. GH_AW_SAFE_OUTPUT_SCRIPTS env var (JSON map of name→filename) is passed to the handler manager step
  3. The handler manager loads and calls main(config) on each script, then dispatches messages of the matching type to the returned handler function
  4. MCP tool definitions are generated the same way as custom safe-jobs

Build, lint, and all 15 new unit tests pass. 174 existing workflows recompile cleanly.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw .cfg 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git -json .cfg 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw owner /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-current git 0/x64/bin/bash git (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw git /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel /usr/bin/git /usr/bin/git git (http block)
  • https://api.github.com/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name pkg/mod/github.c-errorsas pkg/mod/github.c-ifaceassert 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-buildtags -o /tmp/go-build152-errorsas -trimpath 64/bin/go -p main -lang=go1.25 go (http block)
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name --show-toplevel ingutil.test /usr/bin/git -json GO111MODULE ache/go/1.25.0/x/repos/actions/checkout/git/ref/tags/v5 git rev-�� --show-toplevel ache/go/1.25.0/x64/pkg/tool/linux_amd64/link /usr/bin/git 2020-28018/test-git /tmp/go-build236rev-parse 2688770/b429/imp--show-toplevel git (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha nt/action/git/ref/tags/v999.999.999 status /usr/bin/git .github/workflowgit GO111MODULE nch,headSha,disp--show-toplevel git rev-�� --show-toplevel x_amd64/vet /usr/bin/git ub/workflows GO111MODULE x_amd64/vet git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel 0/x64/bin/node /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git ub/workflows git /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha --show-toplevel git /opt/hostedtoolcache/node/24.14.0/x64/bin/node --show-toplevel git /usr/bin/git node js/f�� 0/x64/bin/node git /opt/hostedtoolcache/node/24.14.0/x64/bin/node --show-toplevel git /usr/bin/git node (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha -unreachable=false /tmp/go-build2362688770/b200/vet.cfg /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet 5281312/b434/_pkgit GO111MODULE 64/bin/go /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -ato�� -bool -buildtags /usr/bin/git -errorsas -ifaceassert -nilfunc git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha --show-toplevel git /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/compile --show-toplevel ache/go/1.25.0/xrev-parse /usr/bin/git /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/compile -V=f�� .cfg git /opt/hostedtoolcache/node/24.14.0/x64/bin/node --show-toplevel ache/go/1.25.0/xrev-parse /usr/bin/git node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha --show-toplevel git 0/x64/bin/node --show-toplevel x_amd64/cgo /usr/bin/git git cjs --show-toplevel git ache/node/24.14.0/x64/bin/node --show-toplevel git /usr/bin/git git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha -json GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linu--json env -json GO111MODULE 64/pkg/tool/linu--limit GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel 2688770/b449/_testmain.go /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/link -json GO111MODULE x_amd64/vet /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/link -o /tmp/go-build2362688770/b449/workflow.test -importcfg /usr/bin/git -s -w -buildmode=exe git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/link /usr/bin/git 432584703 2688770/b038/vetrev-parse ortcfg.link git rev-�� --show-toplevel yQm7JrG_JTdGyVRlef/N6GE9dzJuLpfUe9tz4e_/2s_1ls3qXTEKwHQoxHY9 /usr/bin/git ithub/workflows 2688770/b213/vetrev-parse les.test git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --get remote.origin.url /usr/lib/git-core/git -json GO111MODULE 64/bin/go /usr/lib/git-core/git main�� run --auto /usr/bin/git --detach GO111MODULE x_amd64/vet git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --get remote.origin.url e/git -json GO111MODULE 64/bin/go e/git chec�� -b om/owner/repo.git /usr/bin/git -json GO111MODULE x_amd64/vet git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git -json GO111MODULE ache/go/1.25.0/x--show-toplevel git rev-�� --show-toplevel ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet /usr/bin/git 2020-28018/test-git 2688770/b083/vetrev-parse .cfg git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha prettier --check 64/bin/go **/*.ts **/*.json --ignore-path git -c log.showsignatur-errorsas log 64/bin/go --format=%H:%ct (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha &#34;prettier&#34; --check &#39;scripts/**/*GOINSECURE sh 64/bin/go npx prettier --w/opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha &#34;prettier&#34; --che-errorsas npx 64/bin/go tierignore **/*.cjs 64/bin/go go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha user.email test@example.com /usr/bin/git -json GO111MODULE 64/bin/go git conf�� /github.com/owner/repo.git Test User /opt/hostedtoolcache/node/24.14.0/x64/bin/node -json GO111MODULE x_amd64/vet node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel /opt/hostedtoolcrev-parse /usr/bin/git git rev-�� --show-toplevel git ache/node/24.14.0/x64/bin/node --show-toplevel /opt/hostedtoolcrev-parse /usr/bin/git ache/node/24.14.0/x64/bin/node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel git /usr/bin/git git rev-�� --show-toplevel git /opt/hostedtoolcache/node/24.14.0/x64/bin/node --show-toplevel git /usr/bin/git node (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha k/gh-aw/gh-aw/.github/workflows/blog-auditor.md go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --get remote.origin.url /usr/bin/git --show-toplevel /opt/hostedtoolcrev-parse /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel /opt/hostedtoolcrev-parse /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --show-toplevel git bash --show-toplevel git /usr/bin/git git rev-�� --show-toplevel git /opt/hostedtoolcache/node/24.14.0/x64/bin/node --show-toplevel git /usr/bin/git node (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha run --auto $name) { hasDiscussionsEnabled } } --detach git /opt/hostedtoolc/home/REDACTED/work/gh-aw/gh-aw/.github/workflows git rev-�� --show-toplevel node 0/x64/bin/node /usr/bin/git git /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha HEAD node $name) { hasDiscussionsEnabled } } ache/go/1.25.0/xinfocmp git /opt/hostedtoolcxterm-color git rev-�� --show-toplevel node 0/x64/bin/node /usr/bin/git git /usr/bin/git git (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha 2688770/b417/_pkg_.a /tmp/go-build2362688770/b065/vet.cfg (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha --show-toplevel git /usr/bin/infocmp --show-toplevel 64/pkg/tool/linurev-parse /usr/bin/git infocmp -1 runs/20260318-152328-32850/test-3315192615 git 428280/b415/vet.cfg --show-toplevel ache/go/1.25.0/xrev-parse /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha ..feature-branch --stdout 0/x64/bin/node --git-dir git /usr/bin/git git rev-�� --show-toplevel git 0/x64/bin/node --get remote.origin.urrev-parse /usr/bin/git git (http block)
  • https://api.github.com/repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel git ache/node/24.14.--noprofile git rev-�� --show-toplevel git /tmp/TestGetNpmBinPathSetup_GorootOrdering2767095230/001/go/1.25.0/x64/bin/go --show-toplevel git /usr/bin/git go (http block)
  • https://api.github.com/repos/github/gh-aw
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw --jq .visibility --show-toplevel node r: $owner, name: $name) { hasDiscussionsEnabled } } /usr/bin/git git /opt/hostedtoolc/repos/githubnext/agentics/git/ref/tags/# git conf�� user.name Test User 0/x64/bin/node /usr/bin/git git /usr/local/sbin/inspect git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha t0 -buildtags (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha --show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha HEAD git 0/x64/bin/node --show-toplevel git /usr/bin/git git rev-�� --show-toplevel git 0/x64/bin/node /usr/bin/git JjtEFmA/amODhwtkrev-parse /usr/bin/git git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha -bool -buildtags 2688770/b320/vet.cfg -errorsas -ifaceassert -nilfunc /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -ato�� -bool -buildtags /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -errorsas -ifaceassert -nilfunc /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linu^remote\..*\.gh-resolved$ (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel 64/pkg/tool/linurev-parse /usr/bin/git git rev-�� ithub-script/git/ref/tags/v8 git /usr/bin/git --show-toplevel ache/go/1.25.0/xrev-parse /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha HEAD git 0/x64/bin/node --show-toplevel git /usr/bin/git git rev-�� --show-toplevel git 0/x64/bin/node --show-toplevel git /usr/bin/git git (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env hub/workflows .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 git /usr/bin/git --git-dir iEtxJ64HjgrP /usr/bin/git git rev-�� --show-toplevel git util.test --show-toplevel 4FalH7W/O6Ou1LJurev-parse /usr/bin/docker util.test (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 git /usr/bin/head --git-dir x_amd64/vet /usr/bin/git head -500�� /tmp/gh-aw/aw-feature-branch.patch git /usr/bin/git --show-toplevel x_amd64/vet /usr/bin/git git (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 git /usr/bin/git --show-toplevel x_amd64/vet /usr/bin/git git init�� x_amd64/vet git /usr/bin/git --show-toplevel x_amd64/vet /usr/bin/git git (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 git /usr/bin/gh --show-toplevel JbrieQ94-F8q /usr/bin/git gh auth�� status git 64/pkg/tool/linux_amd64/link GOMODCACHE x_amd64/vet /bin/sh 64/pkg/tool/linux_amd64/link (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linuTest User env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 /usr/bin/unpigz /usr/bin/git -c x_amd64/vet /usr/bin/git git rev-�� ai-moderator.md git 64/pkg/tool/linux_amd64/vet --git-dir x_amd64/vet /usr/lib/git-cor--show-toplevel 64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 GO111MODULE 64/pkg/tool/linu-nolocalimports GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linu/tmp/go-build2362688770/b438/_testmain.go env hub/workflows GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 git /usr/bin/infocmp --show-toplevel x_amd64/vet /usr/bin/git infocmp -1 xterm-color git /usr/bin/git --git-dir x_amd64/vet /usr/bin/git git (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 git /usr/bin/gh --show-toplevel x_amd64/vet /usr/bin/git gh auth�� status git /usr/bin/git --show-toplevel x_amd64/vet /usr/lib/git-cor--show-toplevel git (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path pkg/mod/github.cGOSUMDB GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc/tmp/go-build2362688770/b209/vet.cfg -o /tmp/go-build1525281312/b401/_pkGOINSECURE -trimpath 64/bin/go -p github.com/ayman-atomic -lang=go1.24 go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 100 main -lang=go1.25 go env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 6 GOMOD GOMODCACHE x_amd64/compile env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha ty-test.md GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linu12346 /usr/bin/git -bool l /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git assword=$GITHUB_infocmp assword=$GITHUB_-1 /usr/bin/git _9i2epq/EIrZf1RthI0_7U7555L_ (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha --show-toplevel git /usr/bin/git bility_SameInputnode git /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq .object.sha /tmp/go-build1525281312/b417/_pkGOINSECURE -trimpath 64/bin/go -p github.com/githu-atomic -lang=go1.25 go env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v2.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha /tmp/go-build1525281312/b411/_pkGOINSECURE -trimpath 64/bin/go -p main -lang=go1.25 go env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha /tmp/go-build1525281312/b418/_pkGOINSECURE -trimpath 64/bin/go -p github.com/githu-atomic -lang=go1.25 go env -json GO111MODULE x_amd64/link GOINSECURE GOMOD GOMODCACHE x_amd64/link (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha /tmp/go-build1525281312/b400/_pkGOINSECURE -trimpath 64/bin/go -p main -lang=go1.25 go env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v3.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq .object.sha pkg/workflow/dis-json node 64/bin/go prettier --write 64/bin/go go env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/githubnext/agentics/git/ref/tags/
    • Triggering command: /usr/bin/gh gh api /repos/githubnext/agentics/git/ref/tags/# --jq .object.sha --show-toplevel git /usr/bin/git --show-toplevel git iptables git rev-�� --show-toplevel git /usr/bin/git --show-toplevel git /opt/hostedtoolcgraphql git (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha -json GO111MODULE At,event,headBranch,headSha,displayTitle GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 3662776422/.github/workflows .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linu4 /usr/bin/git -bool -buildtags ipts.test git rev-�� --show-toplevel ipts.test /usr/bin/git /tmp/gh-aw-add-ggit config /usr/bin/git git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha --show-toplevel 0/x64/bin/node /usr/bin/git bility_SameInputnode git /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git --show-toplevel l /usr/bin/git git (http block)
  • https://api.github.com/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env -json .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion --show-toplevel x_amd64/vet /usr/bin/git git rev-�� --show-toplevel /usr/bin/git /usr/bin/git --get-regexp ^remote\..*\.gh-run /usr/bin/git git (http block)
  • https://api.github.com/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc/tmp/go-build2362688770/b215/vet.cfg -o /tmp/go-build1525281312/b391/_pkGOINSECURE -trimpath 64/bin/go -p main -lang=go1.25 go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc/tmp/go-build2362688770/b202/vet.cfg -o /tmp/go-build1525281312/b420/_pkGOINSECURE -trimpath 64/bin/go -p github.com/githu-atomic -lang=go1.25 go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo /usr/bin/git -json GO111MODULE /opt/hostedtoolc--show-toplevel git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.0/x-tests /usr/bin/git -unreachable=falgit /tmp/go-build236rev-parse 0/x64/bin/node git (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build2362688770/b399/cli.test /tmp/go-build2362688770/b399/cli.test -test.testlogfile=/tmp/go-build2362688770/b399/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOSUMDB GOWORK 64/bin/go node /hom�� --check scripts/**/*.js 64/bin/go .prettierignore --write 64/bin/go go (http block)
    • Triggering command: /tmp/go-build548428280/b001/cli.test /tmp/go-build548428280/b001/cli.test -test.paniconexit0 -test.timeout=10m0s -test.count=1 rev-�� /ref/tags/v8 ache/go/1.25.0/x64/pkg/tool/linu12345 /usr/bin/git -json GO111MODULE trepo.git git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet /usr/bin/git -unreachable=falgit /tmp/go-build236rev-parse 2688770/b274/vet--show-toplevel git (http block)
  • https://api.github.com/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name k/gh-aw/gh-aw/pk-errorsas GOPROXY 64/bin/go GOSUMDB GOWORK run-script/lib/n-bool /opt/hostedtoolc-buildtags -o /tmp/go-build152-errorsas -trimpath 64/bin/go -p github.com/githu-atomic -lang=go1.25 go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name --show-toplevel ache/go/1.25.0/x64/pkg/tool/linu--json /usr/bin/git vaScript17861412git GO111MODULE 2688770/b418/log--show-toplevel git rev-�� --show-toplevel 2688770/b418/logger.test /usr/bin/git t0 -trimpath (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Add support for defined custom safe-output for scripts Add support for defined custom safe-output scripts Mar 18, 2026
@pelikhan
Copy link
Contributor

@copilot use the same trick as in MCP-scripts or actions/github-script and let the user write the body of main directly , then wrap it with main when writing the JavaScript file in the action.
So the user does not have to generate a exports section and main declaration

…only

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot use the same trick as in MCP-scripts or actions/github-script and let the user write the body of main directly , then wrap it with main when writing the JavaScript file in the action.
So the ...

Done in bda995e. Users now write only the body of main (the handler setup code + return statement), and the compiler wraps it with the function declaration and module.exports — same pattern as MCP-scripts and github-script.

Before (required boilerplate):

async function main(config = {}) {
  const { channel } = config;
  return async (msg) => ({ success: true });
}
module.exports = { main };

After (just the body):

const { channel } = config;
return async (msg) => ({ success: true });

The compiler generates the full module file with the async function main(config = {}) { ... } wrapper and module.exports = { main }; automatically.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha -unreachable=false /tmp/go-build2017224207/b060/vet.cfg 7224207/b248/vet.cfg (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha get .cfg 64/pkg/tool/linux_amd64/vet pull.rebase (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha -bool -buildtags /usr/local/sbin/bash -errorsas -ifaceassert -nilfunc bash --no�� --noprofile -tests /usr/bin/git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel x_amd64/vet /usr/bin/git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --noprofile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha se 7224207/b263/vet.cfg k/_temp/uv-python-dir/bash (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha --noprofile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha --show-toplevel (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha --noprofile (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --noprofile (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@pelikhan
Copy link
Contributor

@copilot update smoke-claude to use safe-output scripts to post a ficticious slack message.

…scripts to schema

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot update smoke-claude to use safe-output scripts to post a ficticious slack message.

Done in 9de4b93. smoke-claude now has a post-slack-message safe-output script and a test step (#11) that exercises it:

scripts:
  post-slack-message:
    description: Post a message to a fictitious Slack channel (smoke test only)
    inputs:
      channel: { type: string, required: true }
      message: { type: string, required: true }
    script: |
      const { channel, message } = config;
      return async function handlePostSlackMessage(item) {
        const targetChannel = item.data?.channel || channel || "#general";
        const text = item.data?.message || message || "(no message)";
        core.info(`[FICTITIOUS SLACK] → ${targetChannel}: ${text}`);
        return { success: true, channel: targetChannel, message: text };
      };

The scripts property was also added to the JSON schema so the compiler recognises it.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/github/gh-aw
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw --jq .visibility (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@pelikhan
Copy link
Contributor

@copilot generate the typescript definition so that users know the typedefinition of config. (put it in a separate file)

@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

Commit pushed: d729804

Generated by Changeset Generator

@github-actions
Copy link
Contributor

Smoke Test (Codex)

PRs: "chore: upgrade gh-aw-firewall from v0.24.2 to v0.24.3"; "chore: bump MCP gateway version to v0.1.18"
GitHub MCP (2 merged PR reads): ✅
Serena MCP (activate + >=3 symbols): ✅
Playwright (github.com title contains GitHub): ✅
Web-fetch (github.com contains GitHub): ✅
File write + cat verify: ✅
Bash build (GOCACHE=/tmp/go-cache GOMODCACHE=/tmp/go-mod make build): ✅
Overall status: PASS

🔮 The oracle has spoken through Smoke Codex ·

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

@github-actions
Copy link
Contributor

Smoke Test Results - Run §23272536774

Test Status
GitHub MCP
MCP Scripts GH CLI
Serena MCP
Playwright
Web Fetch
File Writing
Bash Tool
Discussion Interaction
Build gh-aw
Discussion Creation
Workflow Dispatch
PR Review

Overall: ⚠️ PARTIAL PASS (Serena MCP tools not available)

PR Author: @pelikhan (via copilot-swe-agent) | Assignees: @pelikhan, @Copilot

📰 BREAKING: Report filed by Smoke Copilot ·

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR #21582 — "Add support for defined custom safe-output scripts". The implementation looks clean: SafeScriptConfig is well-structured, the reuse of generateCustomJobToolDefinition is elegant, and deterministic JSON output via sorted keys is a good practice. Two minor inline observations added.

📰 BREAKING: Report filed by Smoke Copilot


// safeOutputScriptFilename returns the .cjs filename for a normalized safe output script name.
func safeOutputScriptFilename(normalizedName string) string {
return "safe_output_script_" + normalizedName + ".cjs"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename format safe_output_script_(name).cjs is clear and consistent with the naming convention. Consider adding a constant for the prefix "safe_output_script_" to avoid potential typos if this string is referenced elsewhere.

Inputs: scriptConfig.Inputs,
}
return generateCustomJobToolDefinition(scriptName, jobConfig)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice reuse of generateCustomJobToolDefinition by adapting the script config into a SafeJobConfig. This keeps tool schema generation DRY. 👍

@github-actions
Copy link
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@github-actions
Copy link
Contributor

Agent Container Tool Check

Tool Status Version
bash 5.2.21
sh available
git 2.53.0
jq 1.7
yq v4.52.4
curl 8.5.0
gh 2.87.3
node v20.20.1
python3 3.12.3
go 1.24.13
java 21.0.10
dotnet 10.0.102

Result: 12/12 tools available ✅

Overall Status: PASS

🔧 Tool validation by Agent Container Smoke Test ·

@github-actions
Copy link
Contributor

Smoke Test Results — Run §23272536740 — Status: PARTIAL (2 skipped)

Test Test
1 GitHub MCP 2 GH CLI
3 Serena MCP 4 Make Build
5 Playwright 6 Tavily Search
7 File Write 8 Bash
9 Discussion 10 AW MCP
11 Slack Output 12 Update PR
13 Review Comments 14 Submit Review
15 Resolve Thread ⚠️ 16 Add Reviewer
17 Push Branch 18 Close PR ⚠️

💥 [THE END] — Illustrated by Smoke Claude ·

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💥 Automated smoke test review - all systems nominal!

💥 [THE END] — Illustrated by Smoke Claude

Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"`
Inputs map[string]*InputDefinition `yaml:"inputs,omitempty"`
Script string `yaml:"script,omitempty"` // Inline JavaScript handler (must export a main factory function)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Script field comment "must export a main factory function" is useful — consider also documenting the expected signature in the comment, e.g., async function main(config) => async (item, resolvedTemporaryIds, temporaryIdMap) => result for completeness.

// SafeScriptConfig defines a custom safe output handler script that runs in the handler loop.
// Scripts run within the consolidated safe-outputs job as part of the handler manager,
// unlike SafeJobConfig which creates a separate GitHub Actions job.
type SafeScriptConfig struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice struct layout. Consider whether Script should be validated at parse time (e.g., non-empty check) to surface misconfigured scripts early rather than at runtime.

@pelikhan
Copy link
Contributor

@copilot ensure that https://github.com/github/gh-aw/actions/runs/23272536740 has the proper tools and safe output call.

Copy link
Contributor Author

Copilot AI commented Mar 19, 2026

@copilot ensure that https://github.com/github/gh-aw/actions/runs/23272536740 has the proper tools and safe output call.

The run at https://github.com/github/gh-aw/actions/runs/23272536740 has everything working correctly:

  • Tool registered: GH_AW_SAFE_OUTPUT_SCRIPTS: {"post_slack_message":"safe_output_script_post_slack_message.cjs"} with ✓ Loaded and initialized custom script handler for: post_slack_message
  • Agent called the tool (message 2/11): [FICTITIOUS SLACK] → #smoke-tests: 💥 Smoke test 23272536740 passed — Claude engine nominal!✓ Message 2 (post_slack_message) completed successfully
  • Generated handler correctly includes temporaryIdMap as 3rd argument: handlePostSlackMessage(item, resolvedTemporaryIds, temporaryIdMap)
  • All 11/11 messages processed, 0 failed

@pelikhan pelikhan marked this pull request as ready for review March 19, 2026 00:03
Copilot AI review requested due to automatic review settings March 19, 2026 00:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new safe-outputs.scripts feature for defining inline JavaScript safe-output handlers that run inside the consolidated safe-outputs handler-manager loop (without spawning separate GitHub Actions jobs), including schema/tooling/runtime support and examples.

Changes:

  • Add safe-outputs.scripts parsing/config plumbing and include scripts in “enabled safe outputs” state + tool generation.
  • Generate wrapped .cjs modules for each script at runtime and load/dispatch them from the handler manager via GH_AW_SAFE_OUTPUT_SCRIPTS.
  • Add schema + TypeScript authoring types and update smoke-claude to exercise a script tool.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pkg/workflow/safe_scripts.go Adds script config parsing + env-var JSON builder + tool-definition adapter.
pkg/workflow/safe_scripts_test.go Unit tests for parsing, JSON mapping, tool defs, wrapper generation, and step/env emission.
pkg/workflow/safe_outputs_tools_filtering.go Includes script tools in filtered + dynamic tool JSON.
pkg/workflow/safe_outputs_state.go Treats scripts as enabled/non-builtin safe outputs.
pkg/workflow/safe_outputs_config.go Extracts safe-outputs.scripts from frontmatter.
pkg/workflow/safe_outputs_config_generation.go Emits script metadata into GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG.
pkg/workflow/compiler_types.go Adds Scripts field to SafeOutputsConfig.
pkg/workflow/compiler_safe_outputs_steps.go Adds GH_AW_SAFE_OUTPUT_SCRIPTS env var to handler-manager step.
pkg/workflow/compiler_safe_outputs_job.go Generates wrapped script module content + writes files via heredoc step.
actions/setup/js/safe_output_helpers.cjs Adds loadCustomSafeOutputScriptHandlers() to parse env var mapping.
actions/setup/js/safe_output_handler_manager.cjs Loads/initializes custom script handlers and dispatches by message type.
pkg/parser/schemas/main_workflow_schema.json Adds schema for safe-outputs.scripts and its fields.
actions/setup/js/types/safe-output-script.d.ts Adds TS types/docs for script authors.
.github/workflows/smoke-claude.md Adds a post-slack-message script and a smoke test step using the new tool.
.github/workflows/smoke-claude.lock.yml Regenerated lockfile reflecting the new script and tool metadata.
.changeset/patch-add-safe-output-scripts.md Changeset entry for the new feature.
Comments suppressed due to low confidence (1)

pkg/workflow/compiler_safe_outputs_job.go:569

  • main(config) is currently passed an object shaped like { description, inputs } (from GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG), so destructuring per-input values from config (const { channel, message } = config;) will always yield undefined. Either (a) change the generated wrapper/docs to source defaults from config.inputs.<name>.default, or (b) include top-level per-input default values in the script’s config object so the destructured locals are meaningful.
// generateSafeOutputScriptContent generates a complete JavaScript module for a custom safe-output
// script handler. Users write only the handler body (the code that runs inside the async handler
// function for each item), and the compiler generates the full outer wrapper including:
//   - Config input destructuring: const { channel, message } = config;
//   - Handler function: return async function handleX(item, resolvedTemporaryIds) { ... }
//   - The module.exports boilerplate
func generateSafeOutputScriptContent(scriptName string, scriptConfig *SafeScriptConfig) string {
	var sb strings.Builder
	sb.WriteString("// @ts-check\n")
	sb.WriteString("/// <reference types=\"./safe-output-script\" />\n")
	sb.WriteString("// Auto-generated safe-output script handler: " + scriptName + "\n\n")
	sb.WriteString("/** @type {import('./types/safe-output-script').SafeOutputScriptMain} */\n")
	sb.WriteString("async function main(config = {}) {\n")

	// Auto-destructure all declared input names from config (provides access to
	// static YAML config values such as defaults).
	if len(scriptConfig.Inputs) > 0 {
		inputNames := make([]string, 0, len(scriptConfig.Inputs))
		for name := range scriptConfig.Inputs {
			safeName := stringutil.SanitizeParameterName(name)
			if safeName != name {
				inputNames = append(inputNames, name+": "+safeName)
			} else {
				inputNames = append(inputNames, name)
			}
		}
		sort.Strings(inputNames)
		sb.WriteString("  const { " + strings.Join(inputNames, ", ") + " } = config;\n")
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +558 to +568
inputNames := make([]string, 0, len(scriptConfig.Inputs))
for name := range scriptConfig.Inputs {
safeName := stringutil.SanitizeParameterName(name)
if safeName != name {
inputNames = append(inputNames, name+": "+safeName)
} else {
inputNames = append(inputNames, name)
}
}
sort.Strings(inputNames)
sb.WriteString(" const { " + strings.Join(inputNames, ", ") + " } = config;\n")
"description": inputDef.Description,
"required": inputDef.Required,
}
if inputDef.Default != "" {
Comment on lines +7538 to +7589
"scripts": {
"type": "object",
"description": "Inline JavaScript script handlers that run inside the consolidated safe-outputs job handler loop. Unlike 'jobs' (which create separate GitHub Actions jobs), scripts execute in-process alongside the built-in handlers. Users write only the body of the main function — the compiler wraps it with 'async function main(config = {}) { ... }' and 'module.exports = { main };' automatically. Script names containing dashes will be automatically normalized to underscores (e.g., 'post-slack-message' becomes 'post_slack_message').",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_-]*$": {
"type": "object",
"description": "Inline script handler configuration. The script body has access to 'config' (the handler config object) and should return an async function that processes individual safe-output messages.",
"properties": {
"name": {
"type": "string",
"description": "Display name for the script handler"
},
"description": {
"type": "string",
"description": "Description of the script handler (used in MCP tool registration)"
},
"inputs": {
"type": "object",
"description": "Input parameters for the script handler",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_-]*$": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Input parameter description"
},
"required": {
"type": "boolean",
"description": "Whether this input is required",
"default": false
},
"default": {
"description": "Default value for the input",
"oneOf": [{ "type": "string" }, { "type": "boolean" }, { "type": "number" }]
},
"type": {
"type": "string",
"enum": ["string", "boolean", "number"],
"description": "Input parameter type",
"default": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"script": {
"type": "string",
"description": "JavaScript handler body. Write only the code that runs inside the handler for each item — the compiler generates the full outer wrapper including config input destructuring (`const { channel, message } = config;`) and the handler function (`return async function handleX(item, resolvedTemporaryIds) { ... }`). The body has access to `item` (runtime message with input values), `resolvedTemporaryIds` (map of temporary IDs), and config-destructured local variables for each declared input."
}
Comment on lines +165 to +185
// Load custom script handlers from GH_AW_SAFE_OUTPUT_SCRIPTS
// These are inline scripts defined in safe-outputs.scripts that run in the handler loop
const customScriptHandlers = loadCustomSafeOutputScriptHandlers();
if (customScriptHandlers.size > 0) {
core.info(`Loading ${customScriptHandlers.size} custom script handler(s): ${[...customScriptHandlers.keys()].join(", ")}`);
for (const [scriptType, scriptFilename] of customScriptHandlers) {
const scriptPath = require("path").join(process.env.RUNNER_TEMP || "/tmp", "gh-aw", "actions", scriptFilename);
try {
const scriptModule = require(scriptPath);
if (scriptModule && typeof scriptModule.main === "function") {
const handlerConfig = config[scriptType] || {};
const messageHandler = await scriptModule.main(handlerConfig);
if (typeof messageHandler !== "function") {
// Non-fatal: warn and skip this custom script handler rather than crashing the
// entire safe-output loop. A misconfigured user script should not block all
// other safe-output operations.
core.warning(`✗ Custom script handler ${scriptType} main() did not return a function (got ${typeof messageHandler}) — this handler will be skipped`);
} else {
messageHandlers.set(scriptType, messageHandler);
core.info(`✓ Loaded and initialized custom script handler for: ${scriptType}`);
}
Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"`
Inputs map[string]*InputDefinition `yaml:"inputs,omitempty"`
Script string `yaml:"script,omitempty"` // Inline JavaScript handler (must export a main factory function)
Comment on lines +79 to +88
if safeOutputs, exists := frontmatter["safe-outputs"]; exists {
if safeOutputsMap, ok := safeOutputs.(map[string]any); ok {
if scripts, exists := safeOutputsMap["scripts"]; exists {
if scriptsMap, ok := scripts.(map[string]any); ok {
return parseSafeScriptsConfig(scriptsMap)
}
}
}
}
return make(map[string]*SafeScriptConfig)
@pelikhan pelikhan merged commit 8774d6c into main Mar 19, 2026
57 checks passed
@pelikhan pelikhan deleted the copilot/add-support-defined-custom-safe-output branch March 19, 2026 00:46
github-actions bot added a commit that referenced this pull request Mar 19, 2026
- Add inline JavaScript script handlers (`safe-outputs.scripts`) section
  to custom-safe-outputs.md, documenting the new in-process handler
  feature added in #21582
- Fix github-tools.md guard policy description: repos defaults to "all"
  when omitted, only min-integrity is required (#21718)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
github-actions bot added a commit that referenced this pull request Mar 19, 2026
- Add Safe Output Scripts term (safe-outputs.scripts, #21582)
- Update Guard Policy: repos now defaults to 'all' when omitted (#21718)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants