diff --git a/docs/adr/33658-first-class-engine-permission-mode-field.md b/docs/adr/33658-first-class-engine-permission-mode-field.md new file mode 100644 index 00000000000..54e64a28c59 --- /dev/null +++ b/docs/adr/33658-first-class-engine-permission-mode-field.md @@ -0,0 +1,86 @@ +# ADR-33658: First-Class `engine.permission-mode` for Claude Engine + +**Date**: 2026-05-21 +**Status**: Draft +**Deciders**: Unknown (draft generated from PR #33658 diff) + +--- + +## Part 1 — Narrative (Human-Friendly) + +### Context + +The Claude CLI accepts a `--permission-mode` flag that gates whether tool requests are auto-approved (`bypassPermissions`), interactively confirmed (`auto`), silently accepted for edits (`acceptEdits`), or restricted to planning (`plan`). gh-aw selects this flag implicitly: the `hasBashWildcardInTools` heuristic forces `bypassPermissions` whenever a `bash: ["*"]` entry is detected in the tool list. The network-egress firewall injects exactly such a wildcard at compile time, which means any workflow running with the firewall enabled was silently switched to `bypassPermissions` — making the `--allowed-tools` boundary configured by `safe-outputs` and `tools` effectively a no-op. The only known override was a fragile `engine.args` workaround that relied on the Claude CLI's last-flag-wins parsing to overwrite the auto-selected mode. There was no explicit, validated configuration surface for workflow authors to decouple the permission mode from the firewall. + +### Decision + +We will add `permission-mode` as a first-class field on `EngineConfig` for the Claude engine. The field is extracted from frontmatter (`engine.permission-mode`) in both the standard and inline-engine code paths, validated by a new `validateEnginePermissionMode` against the closed set `{auto, acceptEdits, plan, bypassPermissions}`, and surfaced in the workflow JSON schema (`engine_config` variants 1 and 2) with an `enum` constraint plus a regenerated `autocomplete-data.json`. At compile time, `claude_engine.go` consults the explicit field first; only when it is unset does the existing `hasBashWildcardInTools` fallback apply. The primary driver is restoring the integrity of `--allowed-tools` enforcement under the firewall by making the permission boundary an explicit, validated workflow concern rather than an implicit side effect of bash-tool wildcards. + +### Alternatives Considered + +#### Alternative 1: Keep the `engine.args` workaround as the supported override + +Workflow authors could continue to override the auto-selected permission mode by appending `--permission-mode auto` (or similar) to `engine.args`, relying on Claude CLI's documented last-flag-wins behavior. This was rejected because it is undocumented in gh-aw, has no schema or validation (typos like `--permission-modes` produce silent breakage), and depends on internal CLI parsing semantics that are not part of any stability contract. It also offers no way for the compiler or downstream tooling to reason about the effective permission boundary. + +#### Alternative 2: Remove the `hasBashWildcardInTools` auto-selection entirely + +The auto-selection could be deleted so that the firewall's `bash: ["*"]` injection no longer flips the permission mode, leaving `auto` as the unconditional default. This was rejected for backwards compatibility: existing workflows that intentionally request unrestricted bash and rely on the implicit `bypassPermissions` behavior (without setting any explicit `permission-mode`) would suddenly hit interactive permission prompts that hang non-interactive CI runs. Layering explicit-wins-over-implicit preserves the safety hatch while letting authors opt into the stricter mode. + +#### Alternative 3: Tie the permission mode to firewall state directly + +The compiler could detect that the firewall feature is enabled and force a stricter default in that case (e.g. `auto` when firewall is on, current behavior otherwise). This was rejected because it couples two orthogonal concerns — network egress policy and Claude tool-permission policy — making the effective configuration depend on the interaction of two features rather than a single, locally visible workflow field. It also leaves no escape hatch for authors who legitimately want `bypassPermissions` under the firewall. + +### Consequences + +#### Positive +- `--allowed-tools` enforcement is once again meaningful for workflows that opt into a non-bypass mode, even with the firewall enabled. +- Schema and validation make typos and unsupported values fail fast at compile time with a documented error message, instead of silently producing a different runtime behavior. +- Editor autocomplete (`autocomplete-data.json`) and schema (`main_workflow_schema.json`) now advertise the field and its allowed values, improving discoverability. +- The new field is local and self-contained — workflow authors can audit a single frontmatter line to know the effective permission boundary instead of reasoning about the bash-wildcard heuristic. + +#### Negative +- Workflow authors gain a new field that interacts subtly with `hasBashWildcardInTools`: the explicit value always wins, so an author who sets `permission-mode: auto` together with a `bash: ["*"]` tool produces a stricter mode than the silent default, and may be surprised by interactive prompts in a non-interactive runner if they forget about the implication. +- The Claude CLI's permission-mode vocabulary (`auto`, `acceptEdits`, `plan`, `bypassPermissions`) is now part of gh-aw's stable surface; if upstream Claude renames or removes a mode, the schema enum and validator will need a coordinated update. +- Two compiler entry points (`compiler_orchestrator_workflow.go` and `compiler_string_api.go`) and two parsing paths (standard frontmatter vs inline engine definition) must each plumb the new field and call the validator — drift between them would let invalid configs through in one path and not the other. + +#### Neutral +- The field applies only to the Claude engine; other engines (Codex, OpenCode, etc.) ignore the value. Workflow authors targeting multiple engines must understand that `permission-mode` is Claude-specific. +- The autocomplete data file is regenerated from the schema; future schema changes to `engine_config` variants must regenerate this artifact or the editor experience drifts from runtime validation. + +--- + +## Part 2 — Normative Specification (RFC 2119) + +> The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this section are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). + +### Configuration Surface + +1. The `EngineConfig` struct **MUST** expose a `PermissionMode string` field that is populated from the frontmatter key `engine.permission-mode` in both the standard frontmatter and inline-engine-definition parsing paths. +2. The workflow JSON schema (`main_workflow_schema.json`) **MUST** define `permission-mode` on the `engine_config` object variants that accept the Claude engine, and the field **MUST** be constrained to an `enum` whose members are exactly `auto`, `acceptEdits`, `plan`, and `bypassPermissions`. +3. The editor `autocomplete-data.json` artifact **MUST** be regenerated whenever the schema enum membership changes, so that completion suggestions match the validator. +4. Implementations **MUST NOT** silently coerce or rewrite the field value; an unrecognized value is a configuration error, not a value to be normalized. + +### Validation + +1. The compiler **MUST** invoke `validateEnginePermissionMode` from both compiler orchestration entry points (`compiler_orchestrator_workflow.go` and `compiler_string_api.go`) before producing the workflow YAML. +2. If `engine.permission-mode` is set to a value outside `{auto, acceptEdits, plan, bypassPermissions}`, the compiler **MUST** return a validation error that names the offending value and lists the accepted set. +3. If `engine.permission-mode` is unset or empty, the validator **MUST** treat it as absent and **MUST NOT** raise an error. + +### Runtime Effect on Claude CLI Invocation + +1. The Claude engine builder **MUST** emit `--permission-mode ` exactly once on the Claude CLI command line. +2. When `engine.permission-mode` is set, the emitted value **MUST** equal that configured value, regardless of whether `hasBashWildcardInTools` would otherwise return true. +3. When `engine.permission-mode` is unset, the builder **MUST** preserve the prior behavior: emit `bypassPermissions` if `hasBashWildcardInTools` is true, otherwise the documented default (`auto`). +4. Implementations **MUST NOT** introduce a third mechanism (additional flag rewriting, environment-variable override, post-hoc patching of `engine.args`) to influence `--permission-mode` once the explicit field and the bash-wildcard fallback have been resolved. + +### Scope + +1. The `engine.permission-mode` field **SHALL** apply only to the Claude engine. Other engines **MAY** ignore the value without error, and **MUST NOT** repurpose it for unrelated configuration. + +### Conformance + +An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance. + +--- + +*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/26203507133) workflow. The PR author must review, complete, and finalize this document before the PR can merge.* diff --git a/docs/public/editor/autocomplete-data.json b/docs/public/editor/autocomplete-data.json index b46c371f060..887025852c1 100644 --- a/docs/public/editor/autocomplete-data.json +++ b/docs/public/editor/autocomplete-data.json @@ -59,7 +59,10 @@ "inlined-imports": { "type": "boolean", "desc": "If true, inline all imports (including those without inputs) at compilation time in the generated lock.yml instead of...", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "on": { @@ -81,6 +84,11 @@ "desc": "On Label Command trigger: fires when a specific label is added to an issue, pull request, or discussion.", "leaf": true }, + "pull_request_reviewer": { + "type": "null|string", + "desc": "Experimental synthetic reviewer lifecycle trigger.", + "leaf": true + }, "push": { "type": "object", "desc": "Push event trigger that runs the workflow when code is pushed to the repository" @@ -240,7 +248,9 @@ "skip-if-check-failing": { "type": "null|boolean|object", "desc": "Skip workflow execution if any CI checks on the target branch are failing or pending.", - "enum": [true], + "enum": [ + true + ], "leaf": true }, "skip-roles": { @@ -262,7 +272,15 @@ "roles": { "type": "string|array", "desc": "Repository access roles required to trigger agentic workflows.", - "enum": ["admin", "maintainer", "maintain", "write", "triage", "read", "all"], + "enum": [ + "admin", + "maintainer", + "maintain", + "write", + "triage", + "read", + "all" + ], "leaf": true, "array": true }, @@ -280,7 +298,10 @@ "allow-bot-authored-trigger-comment": { "type": "boolean", "desc": "Allow the bot-posted-menu / user-checks-box pattern: when a workflow posts a checkbox-menu comment as a GitHub App bo...", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "manual-approval": { @@ -291,7 +312,17 @@ "reaction": { "type": "string|integer|object", "desc": "AI reaction to add/remove on triggering item.", - "enum": ["+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes", "none"], + "enum": [ + "+1", + "-1", + "laugh", + "confused", + "heart", + "hooray", + "rocket", + "eyes", + "none" + ], "leaf": true }, "status-comment": { @@ -325,7 +356,10 @@ "stale-check": { "type": "boolean", "desc": "When set to false, disables the frontmatter hash check step in the activation job.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true } } @@ -333,120 +367,195 @@ "permissions": { "type": "string|object", "desc": "GitHub token permissions for the workflow.", - "enum": ["read-all", "write-all"], + "enum": [ + "read-all", + "write-all" + ], "children": { "actions": { "type": "string", "desc": "Permission for GitHub Actions workflows and runs (read: view workflows, write: manage workflows, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "attestations": { "type": "string", "desc": "Permission for artifact attestations (read: view attestations, write: create attestations, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "checks": { "type": "string", "desc": "Permission for repository checks and status checks (read: view checks, write: create/update checks, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "contents": { "type": "string", "desc": "Permission for repository contents (read: view files, write: modify files/branches, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "deployments": { "type": "string", "desc": "Permission for repository deployments (read: view deployments, write: create/update deployments, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "discussions": { "type": "string", "desc": "Permission for repository discussions (read: view discussions, write: create/update discussions, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "id-token": { "type": "string", "desc": "Permission level for OIDC token requests (write/none only - read is not supported).", - "enum": ["write", "none"], + "enum": [ + "write", + "none" + ], "leaf": true }, "issues": { "type": "string", "desc": "Permission for repository issues (read: view issues, write: create/update/close issues, none: no access)", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "models": { "type": "string", "desc": "Permission for GitHub Copilot models (read: access AI models for agentic workflows, none: no access)", - "enum": ["read", "none"], + "enum": [ + "read", + "none" + ], "leaf": true }, "metadata": { "type": "string", "desc": "Permission for repository metadata (read: view repository information, write: update repository metadata, none: no ac...", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "packages": { "type": "string", "desc": "Permission level for GitHub Packages (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "pages": { "type": "string", "desc": "Permission level for GitHub Pages (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "pull-requests": { "type": "string", "desc": "Permission level for pull requests (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "repository-projects": { "type": "string", "desc": "Permission level for repository projects (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "organization-projects": { "type": "string", "desc": "Permission level for organization projects (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "security-events": { "type": "string", "desc": "Permission level for security events (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "statuses": { "type": "string", "desc": "Permission level for commit statuses (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "vulnerability-alerts": { "type": "string", "desc": "Permission level for Dependabot vulnerability alerts (read/write/none).", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "leaf": true }, "all": { "type": "string", "desc": "Permission shorthand that applies read access to all permission scopes.", - "enum": ["read"], + "enum": [ + "read" + ], "leaf": true } } @@ -499,13 +608,19 @@ "cancel-in-progress": { "type": "boolean", "desc": "Whether to cancel in-progress workflows in the same concurrency group when a new one starts.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "queue": { "type": "string", "desc": "Pending run queue behavior for this concurrency group.", - "enum": ["single", "max"], + "enum": [ + "single", + "max" + ], "leaf": true }, "job-discriminator": { @@ -523,7 +638,10 @@ "inline-sub-agents": { "type": "boolean", "desc": "Deprecated switch for inline sub-agent support.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "features": { @@ -541,7 +659,10 @@ "storage": { "type": "string", "desc": "Storage backend for experiment state.", - "enum": ["cache", "repo"], + "enum": [ + "cache", + "repo" + ], "leaf": true } } @@ -549,13 +670,19 @@ "infer": { "type": "boolean", "desc": "DEPRECATED: Use 'disable-model-invocation' instead.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "disable-model-invocation": { "type": "boolean", "desc": "Controls whether the custom agent should disable model invocation.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "secrets": { @@ -631,7 +758,9 @@ "network": { "type": "string|object", "desc": "Network access control for AI engines using ecosystem identifiers and domain allowlists.", - "enum": ["defaults"], + "enum": [ + "defaults" + ], "children": { "allowed": { "type": "array", @@ -641,7 +770,10 @@ "allowed-input": { "type": "boolean", "desc": "When true and the workflow uses workflow_call, expose a network_allowed string input on the compiled lock file.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "blocked": { @@ -654,29 +786,41 @@ "sandbox": { "type": "string|object", "desc": "Sandbox configuration for AI engines.", - "enum": ["default", "awf"], + "enum": [ + "default", + "awf" + ], "children": { "type": { "type": "string", "desc": "Legacy sandbox type field (use agent instead).", - "enum": ["default", "awf"], + "enum": [ + "default", + "awf" + ], "leaf": true }, "agent": { "type": "boolean|string|object", "desc": "Agent sandbox type: 'awf' uses AWF (Agent Workflow Firewall), or false to disable agent sandbox.", - "enum": ["awf"], + "enum": [ + "awf" + ], "children": { "id": { "type": "string", "desc": "Agent identifier (replaces 'type' field in new format): 'awf' for Agent Workflow Firewall", - "enum": ["awf"], + "enum": [ + "awf" + ], "leaf": true }, "type": { "type": "string", "desc": "Legacy: Sandbox type to use (use 'id' instead)", - "enum": ["awf"], + "enum": [ + "awf" + ], "leaf": true }, "version": { @@ -723,7 +867,10 @@ "enableWeakerNestedSandbox": { "type": "boolean", "desc": "Enable weaker nested sandbox mode (recommended: true for Docker access)", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true } } @@ -762,7 +909,10 @@ "enableWeakerNestedSandbox": { "type": "boolean", "desc": "When true, allows nested sandbox processes to run with relaxed restrictions.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true } } @@ -818,7 +968,10 @@ "domain": { "type": "string", "desc": "Gateway domain for URL generation (default: 'host.docker.internal' when agent is enabled, 'localhost' when disabled)", - "enum": ["localhost", "host.docker.internal"], + "enum": [ + "localhost", + "host.docker.internal" + ], "leaf": true }, "keepalive-interval": { @@ -896,13 +1049,19 @@ "cancel-in-progress": { "type": "boolean", "desc": "Whether to cancel in-progress runs of the same concurrency group.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "queue": { "type": "string", "desc": "Pending run queue behavior for this concurrency group.", - "enum": ["single", "max"], + "enum": [ + "single", + "max" + ], "leaf": true } } @@ -933,7 +1092,9 @@ "type": { "type": "string", "desc": "Authentication type.", - "enum": ["github-oidc"], + "enum": [ + "github-oidc" + ], "leaf": true }, "audience": { @@ -1027,7 +1188,21 @@ "bare": { "type": "boolean", "desc": "When true, disables automatic loading of context and custom instructions by the AI engine.", - "enum": [true, false], + "enum": [ + true, + false + ], + "leaf": true + }, + "permission-mode": { + "type": "string", + "desc": "Claude-only: explicit permission mode for Claude Code CLI.", + "enum": [ + "auto", + "acceptEdits", + "plan", + "bypassPermissions" + ], "leaf": true }, "mcp": { @@ -1078,13 +1253,20 @@ "mode": { "type": "string", "desc": "GitHub access mode.", - "enum": ["gh-proxy", "local", "remote"], + "enum": [ + "gh-proxy", + "local", + "remote" + ], "leaf": true }, "type": { "type": "string", "desc": "GitHub MCP transport type: 'local' (Docker-based, default) or 'remote' (hosted at api.githubcopilot.com)", - "enum": ["local", "remote"], + "enum": [ + "local", + "remote" + ], "leaf": true }, "version": { @@ -1100,19 +1282,28 @@ "read-only": { "type": "boolean", "desc": "Enable read-only mode to restrict GitHub MCP server to read-only operations only", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "lockdown": { "type": "boolean", "desc": "Enable lockdown mode to limit content surfaced from public repositories (only items authored by users with push access).", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "integrity-proxy": { "type": "boolean", "desc": "Controls DIFC proxy injection for pre-agent gh CLI steps when guard policies (min-integrity) are configured.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "github-token": { @@ -1158,21 +1349,34 @@ "allowed-repos": { "type": "string|array", "desc": "Guard policy: repository access configuration.", - "enum": ["all", "public", "${{ github.repository }}"], + "enum": [ + "all", + "public", + "${{ github.repository }}" + ], "leaf": true, "array": true }, "repos": { "type": "string|array", "desc": "Deprecated.", - "enum": ["all", "public", "${{ github.repository }}"], + "enum": [ + "all", + "public", + "${{ github.repository }}" + ], "leaf": true, "array": true }, "min-integrity": { "type": "string", "desc": "Guard policy: minimum required integrity level for repository access.", - "enum": ["none", "unapproved", "approved", "merged"], + "enum": [ + "none", + "unapproved", + "approved", + "merged" + ], "leaf": true }, "blocked-users": { @@ -1206,13 +1410,22 @@ "disapproval-integrity": { "type": "string", "desc": "Guard policy: integrity level assigned when a disapproval reaction is present.", - "enum": ["none", "unapproved", "approved", "merged"], + "enum": [ + "none", + "unapproved", + "approved", + "merged" + ], "leaf": true }, "endorser-min-integrity": { "type": "string", "desc": "Guard policy: minimum integrity level required for an endorser (reactor) to promote content.", - "enum": ["unapproved", "approved", "merged"], + "enum": [ + "unapproved", + "approved", + "merged" + ], "leaf": true }, "github-app": { @@ -1237,7 +1450,10 @@ "ignore-if-missing": { "type": "boolean", "desc": "If true, skip token minting when client-id/private-key resolve to empty strings at runtime.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "owner": { @@ -1301,7 +1517,10 @@ "mode": { "type": "string", "desc": "Integration mode: 'cli' (recommended) installs @playwright/cli via npm for token-efficient CLI invocations — use play...", - "enum": ["cli", "mcp"], + "enum": [ + "cli", + "mcp" + ], "leaf": true } } @@ -1333,13 +1552,19 @@ "restore-only": { "type": "boolean", "desc": "If true, only restore the cache without saving it back.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "scope": { "type": "string", "desc": "Cache restore key scope: 'workflow' (default, only restores from same workflow) or 'repo' (restores from any workflow...", - "enum": ["workflow", "repo"], + "enum": [ + "workflow", + "repo" + ], "leaf": true }, "allowed-extensions": { @@ -1382,7 +1607,10 @@ "footer": { "type": "boolean", "desc": "Controls whether AI-generated footer is added to the managed comment.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "github-token": { @@ -1393,7 +1621,10 @@ "staged": { "type": "boolean", "desc": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true } } @@ -1411,7 +1642,10 @@ "cli-proxy": { "type": "boolean", "desc": "When true, each user-facing MCP server is mounted as a standalone CLI tool on PATH.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "serena": { @@ -1465,13 +1699,19 @@ "create-orphan": { "type": "boolean", "desc": "Create orphaned branch if it doesn't exist (default: true)", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "wiki": { "type": "boolean", "desc": "Use the GitHub Wiki git repository instead of the regular repository.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "allowed-extensions": { @@ -1513,13 +1753,19 @@ "fail-on-cache-miss": { "type": "boolean", "desc": "Fail the workflow if cache entry is not found", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "lookup-only": { "type": "boolean", "desc": "If true, only checks if cache entry exists and skips download", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "name": { @@ -1766,7 +2012,10 @@ "staged": { "type": "boolean", "desc": "If true, emit step summary messages instead of making GitHub API calls (preview mode)", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "env": { @@ -1817,7 +2066,10 @@ "footer": { "type": "boolean", "desc": "Global footer control for all safe outputs.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "activation-comments": { @@ -1828,13 +2080,19 @@ "group-reports": { "type": "boolean", "desc": "When true, creates a parent '[aw] Failed runs' issue that tracks all workflow failures as sub-issues.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "report-failure-as-issue": { "type": "boolean", "desc": "When false, disables creating failure tracking issues when workflows fail.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "failure-issue-repo": { @@ -1850,7 +2108,10 @@ "id-token": { "type": "string", "desc": "Override the id-token permission for the safe-outputs job.", - "enum": ["write", "none"], + "enum": [ + "write", + "none" + ], "leaf": true }, "concurrency-group": { @@ -1932,7 +2193,11 @@ "if-missing": { "type": "string", "desc": "How to handle missing OTLP endpoint/header values at runtime (for example from unset secrets).", - "enum": ["error", "warn", "ignore"], + "enum": [ + "error", + "warn", + "ignore" + ], "leaf": true } } @@ -1999,25 +2264,37 @@ "strict": { "type": "boolean", "desc": "Enable strict mode validation for enhanced security and compliance.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "private": { "type": "boolean", "desc": "Mark the workflow as private, preventing it from being added to other repositories via 'gh aw add'.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "check-for-updates": { "type": "boolean", "desc": "Control whether the compile-agentic version update check runs in the activation job.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "run-install-scripts": { "type": "boolean", "desc": "Allow npm pre/post install scripts to execute during package installation.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "mcp-scripts": { @@ -2031,7 +2308,9 @@ "checkout": { "type": "object|array|boolean", "desc": "Checkout configuration for the agent job.", - "enum": [false], + "enum": [ + false + ], "children": { "repository": { "type": "string", @@ -2061,13 +2340,20 @@ "submodules": { "type": "string|boolean", "desc": "Controls submodule checkout.", - "enum": ["recursive", "true", "false"], + "enum": [ + "recursive", + "true", + "false" + ], "leaf": true }, "lfs": { "type": "boolean", "desc": "Whether to download Git LFS objects.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "token": { @@ -2102,7 +2388,10 @@ "ignore-if-missing": { "type": "boolean", "desc": "If true, skip token minting when client-id/private-key resolve to empty strings at runtime.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "owner": { @@ -2122,181 +2411,300 @@ "administration": { "type": "string", "desc": "Permission level for repository administration (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "codespaces": { "type": "string", "desc": "Permission level for Codespaces (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "codespaces-lifecycle-admin": { "type": "string", "desc": "Permission level for Codespaces lifecycle administration (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "codespaces-metadata": { "type": "string", "desc": "Permission level for Codespaces metadata (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "email-addresses": { "type": "string", "desc": "Permission level for user email addresses (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "environments": { "type": "string", "desc": "Permission level for repository environments (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "git-signing": { "type": "string", "desc": "Permission level for git signing (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "members": { "type": "string", "desc": "Permission level for organization members (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-administration": { "type": "string", "desc": "Permission level for organization administration (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-announcement-banners": { "type": "string", "desc": "Permission level for organization announcement banners (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-codespaces": { "type": "string", "desc": "Permission level for organization Codespaces (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-copilot": { "type": "string", "desc": "Permission level for organization Copilot (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-custom-org-roles": { "type": "string", "desc": "Permission level for organization custom org roles (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-custom-properties": { "type": "string", "desc": "Permission level for organization custom properties (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-custom-repository-roles": { "type": "string", "desc": "Permission level for organization custom repository roles (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-events": { "type": "string", "desc": "Permission level for organization events (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-hooks": { "type": "string", "desc": "Permission level for organization webhooks (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-members": { "type": "string", "desc": "Permission level for organization members management (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-packages": { "type": "string", "desc": "Permission level for organization packages (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-personal-access-token-requests": { "type": "string", "desc": "Permission level for organization personal access token requests (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-personal-access-tokens": { "type": "string", "desc": "Permission level for organization personal access tokens (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-plan": { "type": "string", "desc": "Permission level for organization plan (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-self-hosted-runners": { "type": "string", "desc": "Permission level for organization self-hosted runners (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-user-blocking": { "type": "string", "desc": "Permission level for organization user blocking (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "repository-custom-properties": { "type": "string", "desc": "Permission level for repository custom properties (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "repository-hooks": { "type": "string", "desc": "Permission level for repository webhooks (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "single-file": { "type": "string", "desc": "Permission level for single file access (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "team-discussions": { "type": "string", "desc": "Permission level for team discussions (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "vulnerability-alerts": { "type": "string", "desc": "Permission level for Dependabot vulnerability alerts (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none"], + "enum": [ + "read", + "none" + ], "leaf": true }, "workflows": { "type": "string", "desc": "Permission level for GitHub Actions workflow files (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true } } @@ -2306,7 +2714,10 @@ "current": { "type": "boolean", "desc": "Marks this checkout as the logical current repository for the workflow.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "fetch": { @@ -2318,13 +2729,19 @@ "wiki": { "type": "boolean", "desc": "When true, clones the repository's wiki git instead of the regular repository.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "force-clean-git-credentials": { "type": "boolean", "desc": "When true, persist credentials during checkout, then immediately run a post-checkout cleanup step that removes creden...", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true } }, @@ -2352,7 +2769,10 @@ "ignore-if-missing": { "type": "boolean", "desc": "If true, skip token minting when client-id/private-key resolve to empty strings at runtime.", - "enum": [true, false], + "enum": [ + true, + false + ], "leaf": true }, "owner": { @@ -2372,181 +2792,300 @@ "administration": { "type": "string", "desc": "Permission level for repository administration (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "codespaces": { "type": "string", "desc": "Permission level for Codespaces (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "codespaces-lifecycle-admin": { "type": "string", "desc": "Permission level for Codespaces lifecycle administration (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "codespaces-metadata": { "type": "string", "desc": "Permission level for Codespaces metadata (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "email-addresses": { "type": "string", "desc": "Permission level for user email addresses (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "environments": { "type": "string", "desc": "Permission level for repository environments (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "git-signing": { "type": "string", "desc": "Permission level for git signing (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "members": { "type": "string", "desc": "Permission level for organization members (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-administration": { "type": "string", "desc": "Permission level for organization administration (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-announcement-banners": { "type": "string", "desc": "Permission level for organization announcement banners (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-codespaces": { "type": "string", "desc": "Permission level for organization Codespaces (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-copilot": { "type": "string", "desc": "Permission level for organization Copilot (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-custom-org-roles": { "type": "string", "desc": "Permission level for organization custom org roles (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-custom-properties": { "type": "string", "desc": "Permission level for organization custom properties (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-custom-repository-roles": { "type": "string", "desc": "Permission level for organization custom repository roles (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-events": { "type": "string", "desc": "Permission level for organization events (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-hooks": { "type": "string", "desc": "Permission level for organization webhooks (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-members": { "type": "string", "desc": "Permission level for organization members management (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-packages": { "type": "string", "desc": "Permission level for organization packages (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-personal-access-token-requests": { "type": "string", "desc": "Permission level for organization personal access token requests (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-personal-access-tokens": { "type": "string", "desc": "Permission level for organization personal access tokens (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-plan": { "type": "string", "desc": "Permission level for organization plan (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-self-hosted-runners": { "type": "string", "desc": "Permission level for organization self-hosted runners (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "organization-user-blocking": { "type": "string", "desc": "Permission level for organization user blocking (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "repository-custom-properties": { "type": "string", "desc": "Permission level for repository custom properties (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "repository-hooks": { "type": "string", "desc": "Permission level for repository webhooks (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "single-file": { "type": "string", "desc": "Permission level for single file access (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "team-discussions": { "type": "string", "desc": "Permission level for team discussions (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true }, "vulnerability-alerts": { "type": "string", "desc": "Permission level for Dependabot vulnerability alerts (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none"], + "enum": [ + "read", + "none" + ], "leaf": true }, "workflows": { "type": "string", "desc": "Permission level for GitHub Actions workflow files (read/none; \"write\" is rejected by the compiler).", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "leaf": true } } @@ -2600,4 +3139,4 @@ "runtimes", "jobs" ] -} +} \ No newline at end of file diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index bfd371fb27f..c5b4230354b 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -5,35 +5,53 @@ "description": "JSON Schema for validating agentic workflow frontmatter configuration", "version": "1.0.0", "type": "object", - "required": ["on"], + "required": [ + "on" + ], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256, "description": "Workflow name that appears in the GitHub Actions interface. If not specified, defaults to the filename without extension.", - "examples": ["Copilot Agent PR Analysis", "Dev Hawk", "Smoke Claude"] + "examples": [ + "Copilot Agent PR Analysis", + "Dev Hawk", + "Smoke Claude" + ] }, "description": { "type": "string", "maxLength": 10000, "description": "Optional workflow description that is rendered as a comment in the generated GitHub Actions YAML file (.lock.yml)", - "examples": ["Quickstart for using the GitHub Actions library"] + "examples": [ + "Quickstart for using the GitHub Actions library" + ] }, "emoji": { "type": "string", "description": "Optional emoji to represent the workflow visually in listings and UI surfaces.", - "examples": ["\ud83e\udd16", "\ud83d\udd0d", "\ud83d\ude80"] + "examples": [ + "\ud83e\udd16", + "\ud83d\udd0d", + "\ud83d\ude80" + ] }, "source": { "type": "string", "description": "Optional source reference indicating where this workflow was added from. Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/ci-doctor.md@v1.0.0). Rendered as a comment in the generated lock file.", - "examples": ["githubnext/agentics/workflows/ci-doctor.md", "githubnext/agentics/workflows/daily-perf-improver.md@1f181b37d3fe5862ab590648f25a292e345b5de6"] + "examples": [ + "githubnext/agentics/workflows/ci-doctor.md", + "githubnext/agentics/workflows/daily-perf-improver.md@1f181b37d3fe5862ab590648f25a292e345b5de6" + ] }, "redirect": { "type": "string", "description": "Optional workflow location redirect for updates. Format: workflow spec or GitHub URL (e.g., owner/repo/path@ref or https://github.com/owner/repo/blob/main/path.md). When present, update follows this location and rewrites source.", - "examples": ["githubnext/agentics/workflows/ci-doctor-v2.md@main", "https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor-v2.md"] + "examples": [ + "githubnext/agentics/workflows/ci-doctor-v2.md@main", + "https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor-v2.md" + ] }, "tracker-id": { "type": "string", @@ -41,7 +59,11 @@ "maxLength": 128, "pattern": "^[a-zA-Z0-9_-]+$", "description": "Optional tracker identifier to tag all created assets (issues, discussions, comments, pull requests). Must be at least 8 characters and contain only alphanumeric characters, hyphens, and underscores. This identifier will be inserted in the body/description of all created assets to enable searching and retrieving assets associated with this workflow.", - "examples": ["workflow-2024-q1", "team-alpha-bot", "security_audit_v2"] + "examples": [ + "workflow-2024-q1", + "team-alpha-bot", + "security_audit_v2" + ] }, "labels": { "type": "array", @@ -51,9 +73,18 @@ "minLength": 1 }, "examples": [ - ["automation", "security"], - ["docs", "maintenance"], - ["ci", "testing"] + [ + "automation", + "security" + ], + [ + "docs", + "maintenance" + ], + [ + "ci", + "testing" + ] ] }, "metadata": { @@ -90,7 +121,9 @@ { "type": "object", "description": "Import specification with path and optional inputs", - "required": ["path"], + "required": [ + "path" + ], "additionalProperties": false, "properties": { "path": { @@ -136,7 +169,9 @@ { "type": "object", "description": "Import specification with 'uses'/'with' syntax (mirrors GitHub Actions reusable workflow syntax). 'uses' references the workflow path and 'with' provides input values.", - "required": ["uses"], + "required": [ + "uses" + ], "additionalProperties": false, "properties": { "uses": { @@ -221,7 +256,9 @@ { "type": "object", "description": "Import specification with path and optional inputs", - "required": ["path"], + "required": [ + "path" + ], "additionalProperties": false, "properties": { "path": { @@ -267,7 +304,9 @@ { "type": "object", "description": "Import specification with 'uses'/'with' syntax.", - "required": ["uses"], + "required": [ + "uses" + ], "additionalProperties": false, "properties": { "uses": { @@ -339,10 +378,21 @@ } ], "examples": [ - ["shared/jqschema.md", "shared/reporting.md"], - ["shared/mcp/tavily.md", "shared/jqschema.md", "shared/reporting.md"], - ["../instructions/documentation.instructions.md"], - [".github/agents/my-agent.md"], + [ + "shared/jqschema.md", + "shared/reporting.md" + ], + [ + "shared/mcp/tavily.md", + "shared/jqschema.md", + "shared/reporting.md" + ], + [ + "../instructions/documentation.instructions.md" + ], + [ + ".github/agents/my-agent.md" + ], [ { "path": "shared/discussions-data-fetch.md", @@ -352,7 +402,10 @@ } ], { - "aw": ["shared/common-tools.md", "shared/mcp/tavily.md"] + "aw": [ + "shared/common-tools.md", + "shared/mcp/tavily.md" + ] } ] }, @@ -366,25 +419,45 @@ "pattern": "\\$\\{\\{" } }, - "examples": [["triage-issue.md", "label-issue.md"], ["my-custom-action.yml"], ["shared/helper-action.yml", "close-stale.md"]] + "examples": [ + [ + "triage-issue.md", + "label-issue.md" + ], + [ + "my-custom-action.yml" + ], + [ + "shared/helper-action.yml", + "close-stale.md" + ] + ] }, "inlined-imports": { "type": "boolean", "default": false, "description": "If true, inline all imports (including those without inputs) at compilation time in the generated lock.yml instead of using runtime-import macros. When enabled, the frontmatter hash covers the entire markdown body so any change to the content will invalidate the hash.", - "examples": [true, false] + "examples": [ + true, + false + ] }, "on": { "description": "Workflow triggers that define when the agentic workflow should run. Supports standard GitHub Actions trigger events plus special command triggers for /commands (required)", "examples": [ { "issues": { - "types": ["opened"] + "types": [ + "opened" + ] } }, { "pull_request": { - "types": ["opened", "synchronize"] + "types": [ + "opened", + "synchronize" + ] } }, "workflow_dispatch", @@ -398,7 +471,13 @@ "type": "string", "minLength": 1, "description": "Simple trigger event name (e.g., 'push', 'issues', 'pull_request', 'discussion', 'schedule', 'fork', 'create', 'delete', 'public', 'watch', 'workflow_call'), schedule shorthand (e.g., 'daily', 'weekly'), or slash command shorthand (e.g., '/my-bot' expands to slash_command + workflow_dispatch)", - "examples": ["push", "issues", "workflow_dispatch", "daily", "/my-bot"] + "examples": [ + "push", + "issues", + "workflow_dispatch", + "daily", + "/my-bot" + ] }, { "type": "object", @@ -450,7 +529,16 @@ { "type": "string", "description": "Single event name or '*' for all events. Use GitHub Actions event names: 'issues', 'issue_comment', 'pull_request_comment', 'pull_request', 'pull_request_review_comment', 'discussion', 'discussion_comment'.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] }, { "type": "array", @@ -459,7 +547,16 @@ "items": { "type": "string", "description": "GitHub Actions event name.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] }, "maxItems": 25 } @@ -468,7 +565,10 @@ "strategy": { "type": "string", "description": "Slash command trigger compilation strategy. 'inline' (default) compiles direct comment listeners in this workflow. 'centralized' compiles this workflow as workflow_dispatch-centric and routes slash events via the generated central trigger workflow.", - "enum": ["inline", "centralized"] + "enum": [ + "inline", + "centralized" + ] } }, "additionalProperties": false @@ -521,7 +621,16 @@ { "type": "string", "description": "Single event name or '*' for all events. Use GitHub Actions event names: 'issues', 'issue_comment', 'pull_request_comment', 'pull_request', 'pull_request_review_comment', 'discussion', 'discussion_comment'.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] }, { "type": "array", @@ -530,7 +639,16 @@ "items": { "type": "string", "description": "GitHub Actions event name.", - "enum": ["*", "issues", "issue_comment", "pull_request_comment", "pull_request", "pull_request_review_comment", "discussion", "discussion_comment"] + "enum": [ + "*", + "issues", + "issue_comment", + "pull_request_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment" + ] }, "maxItems": 25 } @@ -601,7 +719,12 @@ { "type": "string", "description": "Single item type or '*' for all types.", - "enum": ["*", "issues", "pull_request", "discussion"] + "enum": [ + "*", + "issues", + "pull_request", + "discussion" + ] }, { "type": "array", @@ -610,7 +733,12 @@ "items": { "type": "string", "description": "Item type.", - "enum": ["*", "issues", "pull_request", "discussion"] + "enum": [ + "*", + "issues", + "pull_request", + "discussion" + ] }, "maxItems": 3 } @@ -623,7 +751,10 @@ "strategy": { "type": "string", "description": "Label command trigger compilation strategy. 'inline' (default) compiles direct labeled listeners in this workflow. 'decentralized' compiles this workflow as workflow_dispatch-centric and routes labeled events via the generated agentic_commands.yml workflow.", - "enum": ["inline", "decentralized"] + "enum": [ + "inline", + "decentralized" + ] } }, "additionalProperties": false @@ -695,25 +826,37 @@ }, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -723,25 +866,37 @@ { "oneOf": [ { - "required": ["paths"], + "required": [ + "paths" + ], "not": { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } }, { - "required": ["paths-ignore"], + "required": [ + "paths-ignore" + ], "not": { - "required": ["paths"] + "required": [ + "paths" + ] } }, { "not": { "anyOf": [ { - "required": ["paths"] + "required": [ + "paths" + ] }, { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } ] } @@ -861,25 +1016,37 @@ "additionalProperties": false, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -889,25 +1056,37 @@ { "oneOf": [ { - "required": ["paths"], + "required": [ + "paths" + ], "not": { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } }, { - "required": ["paths-ignore"], + "required": [ + "paths-ignore" + ], "not": { - "required": ["paths"] + "required": [ + "paths" + ] } }, { "not": { "anyOf": [ { - "required": ["paths"] + "required": [ + "paths" + ] }, { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } ] } @@ -926,7 +1105,26 @@ "description": "Types of issue events", "items": { "type": "string", - "enum": ["opened", "edited", "deleted", "transferred", "pinned", "unpinned", "closed", "reopened", "assigned", "unassigned", "labeled", "unlabeled", "locked", "unlocked", "milestoned", "demilestoned", "typed", "untyped"] + "enum": [ + "opened", + "edited", + "deleted", + "transferred", + "pinned", + "unpinned", + "closed", + "reopened", + "assigned", + "unassigned", + "labeled", + "unlabeled", + "locked", + "unlocked", + "milestoned", + "demilestoned", + "typed", + "untyped" + ] } }, "names": { @@ -964,7 +1162,11 @@ "description": "Types of issue comment events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } }, "lock-for-agent": { @@ -983,7 +1185,21 @@ "description": "Types of discussion events", "items": { "type": "string", - "enum": ["created", "edited", "deleted", "transferred", "pinned", "unpinned", "labeled", "unlabeled", "locked", "unlocked", "category_changed", "answered", "unanswered"] + "enum": [ + "created", + "edited", + "deleted", + "transferred", + "pinned", + "unpinned", + "labeled", + "unlabeled", + "locked", + "unlocked", + "category_changed", + "answered", + "unanswered" + ] } } } @@ -998,7 +1214,11 @@ "description": "Types of discussion comment events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -1027,7 +1247,9 @@ "description": "Optional IANA timezone string for timezone-aware scheduling (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo', 'UTC'). When set, the cron expression is interpreted in the specified timezone instead of UTC." } }, - "required": ["cron"], + "required": [ + "cron" + ], "additionalProperties": false }, "maxItems": 10 @@ -1077,7 +1299,13 @@ }, "type": { "type": "string", - "enum": ["string", "choice", "boolean", "number", "environment"], + "enum": [ + "string", + "choice", + "boolean", + "number", + "environment" + ], "description": "Input type. GitHub Actions supports: string (default), boolean, choice (string with predefined options), number, and environment (string referencing a GitHub environment)" }, "options": { @@ -1111,7 +1339,11 @@ "description": "Types of workflow run events", "items": { "type": "string", - "enum": ["completed", "requested", "in_progress"] + "enum": [ + "completed", + "requested", + "in_progress" + ] } }, "branches": { @@ -1133,25 +1365,37 @@ }, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -1168,7 +1412,15 @@ "description": "Types of release events", "items": { "type": "string", - "enum": ["published", "unpublished", "created", "edited", "deleted", "prereleased", "released"] + "enum": [ + "published", + "unpublished", + "created", + "edited", + "deleted", + "prereleased", + "released" + ] } } } @@ -1183,7 +1435,11 @@ "description": "Types of pull request review comment events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -1198,7 +1454,11 @@ "description": "Types of branch protection rule events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -1213,7 +1473,12 @@ "description": "Types of check run events", "items": { "type": "string", - "enum": ["created", "rerequested", "completed", "requested_action"] + "enum": [ + "created", + "rerequested", + "completed", + "requested_action" + ] } } } @@ -1228,7 +1493,9 @@ "description": "Types of check suite events", "items": { "type": "string", - "enum": ["completed"] + "enum": [ + "completed" + ] } } } @@ -1288,13 +1555,31 @@ "oneOf": [ { "type": "string", - "enum": ["error", "failure", "pending", "success", "inactive", "in_progress", "queued", "waiting"] + "enum": [ + "error", + "failure", + "pending", + "success", + "inactive", + "in_progress", + "queued", + "waiting" + ] }, { "type": "array", "items": { "type": "string", - "enum": ["error", "failure", "pending", "success", "inactive", "in_progress", "queued", "waiting"] + "enum": [ + "error", + "failure", + "pending", + "success", + "inactive", + "in_progress", + "queued", + "waiting" + ] }, "minItems": 1 } @@ -1340,7 +1625,11 @@ "description": "Types of label events", "items": { "type": "string", - "enum": ["created", "edited", "deleted"] + "enum": [ + "created", + "edited", + "deleted" + ] } } } @@ -1355,7 +1644,9 @@ "description": "Types of merge group events", "items": { "type": "string", - "enum": ["checks_requested"] + "enum": [ + "checks_requested" + ] } } } @@ -1370,7 +1661,13 @@ "description": "Types of milestone events", "items": { "type": "string", - "enum": ["created", "closed", "opened", "edited", "deleted"] + "enum": [ + "created", + "closed", + "opened", + "edited", + "deleted" + ] } } } @@ -1488,25 +1785,37 @@ "additionalProperties": false, "oneOf": [ { - "required": ["branches"], + "required": [ + "branches" + ], "not": { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } }, { - "required": ["branches-ignore"], + "required": [ + "branches-ignore" + ], "not": { - "required": ["branches"] + "required": [ + "branches" + ] } }, { "not": { "anyOf": [ { - "required": ["branches"] + "required": [ + "branches" + ] }, { - "required": ["branches-ignore"] + "required": [ + "branches-ignore" + ] } ] } @@ -1516,25 +1825,37 @@ { "oneOf": [ { - "required": ["paths"], + "required": [ + "paths" + ], "not": { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } }, { - "required": ["paths-ignore"], + "required": [ + "paths-ignore" + ], "not": { - "required": ["paths"] + "required": [ + "paths" + ] } }, { "not": { "anyOf": [ { - "required": ["paths"] + "required": [ + "paths" + ] }, { - "required": ["paths-ignore"] + "required": [ + "paths-ignore" + ] } ] } @@ -1553,7 +1874,11 @@ "description": "Types of pull request review events", "items": { "type": "string", - "enum": ["submitted", "edited", "dismissed"] + "enum": [ + "submitted", + "edited", + "dismissed" + ] } } } @@ -1568,7 +1893,10 @@ "description": "Types of registry package events", "items": { "type": "string", - "enum": ["published", "updated"] + "enum": [ + "published", + "updated" + ] } } } @@ -1610,7 +1938,9 @@ "description": "Types of watch events", "items": { "type": "string", - "enum": ["started"] + "enum": [ + "started" + ] } } } @@ -1642,7 +1972,11 @@ }, "type": { "type": "string", - "enum": ["string", "number", "boolean"], + "enum": [ + "string", + "number", + "boolean" + ], "description": "Type of the input parameter" }, "default": { @@ -1684,7 +2018,9 @@ }, { "type": "object", - "required": ["query"], + "required": [ + "query" + ], "properties": { "query": { "type": "string", @@ -1706,7 +2042,9 @@ }, "scope": { "type": "string", - "enum": ["none"], + "enum": [ + "none" + ], "description": "Scope for the search query. Set to 'none' to disable the automatic 'repo:owner/repo' scoping, enabling org-wide or cross-repo queries." } }, @@ -1724,7 +2062,9 @@ }, { "type": "object", - "required": ["query"], + "required": [ + "query" + ], "properties": { "query": { "type": "string", @@ -1737,7 +2077,9 @@ }, "scope": { "type": "string", - "enum": ["none"], + "enum": [ + "none" + ], "description": "Scope for the search query. Set to 'none' to disable the automatic 'repo:owner/repo' scoping, enabling org-wide or cross-repo queries." } }, @@ -1755,7 +2097,9 @@ }, { "type": "boolean", - "enum": [true], + "enum": [ + true + ], "description": "Skip workflow execution if any CI checks on the target branch are currently failing. For pull_request events, checks the base branch. For other events, checks the current ref." }, { @@ -1851,7 +2195,15 @@ "oneOf": [ { "type": "string", - "enum": ["admin", "maintainer", "maintain", "write", "triage", "read", "all"], + "enum": [ + "admin", + "maintainer", + "maintain", + "write", + "triage", + "read", + "all" + ], "description": "Single repository permission level that can trigger the workflow. Use 'all' to allow any authenticated user (\u26a0\ufe0f disables permission checking entirely - use with caution)" }, { @@ -1859,7 +2211,14 @@ "description": "List of repository permission levels that can trigger the workflow. Permission checks are automatically applied to potentially unsafe triggers.", "items": { "type": "string", - "enum": ["admin", "maintainer", "maintain", "write", "triage", "read"], + "enum": [ + "admin", + "maintainer", + "maintain", + "write", + "triage", + "read" + ], "description": "Repository permission level: 'admin' (full access), 'maintainer'/'maintain' (repository management), 'write' (push access), 'triage' (issue management), 'read' (read-only access)" }, "minItems": 1, @@ -1906,11 +2265,24 @@ "oneOf": [ { "type": "string", - "enum": ["+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes", "none"] + "enum": [ + "+1", + "-1", + "laugh", + "confused", + "heart", + "hooray", + "rocket", + "eyes", + "none" + ] }, { "type": "integer", - "enum": [1, -1], + "enum": [ + 1, + -1 + ], "description": "YAML parses +1 and -1 without quotes as integers. These are converted to +1 and -1 strings respectively." }, { @@ -1921,11 +2293,24 @@ "oneOf": [ { "type": "string", - "enum": ["+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes", "none"] + "enum": [ + "+1", + "-1", + "laugh", + "confused", + "heart", + "hooray", + "rocket", + "eyes", + "none" + ] }, { "type": "integer", - "enum": [1, -1], + "enum": [ + 1, + -1 + ], "description": "YAML parses +1 and -1 without quotes as integers. These are converted to +1 and -1 strings respectively." } ], @@ -2012,7 +2397,9 @@ "github-token": { "type": "string", "description": "Custom GitHub token for pre-activation reactions, activation status comments, and skip-if search queries. When specified, overrides the default GITHUB_TOKEN for these operations.", - "examples": ["${{ secrets.MY_GITHUB_TOKEN }}"] + "examples": [ + "${{ secrets.MY_GITHUB_TOKEN }}" + ] }, "github-app": { "$ref": "#/$defs/github_app", @@ -2034,7 +2421,11 @@ "additionalItems": false, "uniqueItems": true, "default": [], - "examples": [["secrets_fetcher"]] + "examples": [ + [ + "secrets_fetcher" + ] + ] }, "steps": { "type": "array", @@ -2146,51 +2537,99 @@ "properties": { "actions": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "checks": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "contents": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "deployments": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "discussions": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "issues": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "packages": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "pages": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "pull-requests": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "repository-projects": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "security-events": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] }, "statuses": { "type": "string", - "enum": ["read", "write", "none"] + "enum": [ + "read", + "write", + "none" + ] } }, "additionalProperties": false @@ -2233,25 +2672,37 @@ { "command": { "name": "mergefest", - "events": ["pull_request_comment"] + "events": [ + "pull_request_comment" + ] } }, { "workflow_run": { - "workflows": ["Dev"], - "types": ["completed"], - "branches": ["copilot/**"] + "workflows": [ + "Dev" + ], + "types": [ + "completed" + ], + "branches": [ + "copilot/**" + ] } }, { "pull_request": { - "types": ["ready_for_review"] + "types": [ + "ready_for_review" + ] }, "workflow_dispatch": null }, { "push": { - "branches": ["main"] + "branches": [ + "main" + ] } } ] @@ -2278,7 +2729,10 @@ "oneOf": [ { "type": "string", - "enum": ["read-all", "write-all"], + "enum": [ + "read-all", + "write-all" + ], "description": "Simple permissions string: 'read-all' (all read permissions) or 'write-all' (all write permissions)" }, { @@ -2289,7 +2743,10 @@ "run-name": { "type": "string", "description": "Custom name for workflow runs that appears in the GitHub Actions interface (supports GitHub expressions like ${{ github.event.issue.title }})", - "examples": ["Deploy to ${{ github.event.inputs.environment }}", "Build #${{ github.run_number }}"] + "examples": [ + "Deploy to ${{ github.event.inputs.environment }}", + "Build #${{ github.run_number }}" + ] }, "jobs": { "type": "object", @@ -2332,10 +2789,14 @@ "additionalProperties": false, "oneOf": [ { - "required": ["uses"] + "required": [ + "uses" + ] }, { - "required": ["run"] + "required": [ + "run" + ] } ], "properties": { @@ -2555,7 +3016,9 @@ "description": "The URL to set as the environment URL in the deployment." } }, - "required": ["name"] + "required": [ + "name" + ] } ], "description": "The GitHub Actions environment this job references. When set, any protection rules for the environment must pass before the job runs. Use this to gate jobs on manual approval workflows." @@ -2668,17 +3131,26 @@ ], "examples": [ "ubuntu-latest", - ["ubuntu-latest", "self-hosted"], + [ + "ubuntu-latest", + "self-hosted" + ], { "group": "larger-runners", - "labels": ["ubuntu-latest-8-cores"] + "labels": [ + "ubuntu-latest-8-cores" + ] } ] }, "runs-on-slim": { "type": "string", "description": "Runner for all framework/generated jobs (activation, pre-activation, safe-outputs, unlock, APM, etc.). Provides a compile-stable override for generated job runners without requiring a safe-outputs section. Overridden by safe-outputs.runs-on when both are set. Defaults to 'ubuntu-slim'. Use this when your infrastructure does not provide the default runner or when you need consistent runner selection across all jobs.", - "examples": ["self-hosted", "ubuntu-latest", "ubuntu-22.04"] + "examples": [ + "self-hosted", + "ubuntu-latest", + "ubuntu-22.04" + ] }, "timeout-minutes": { "description": "Workflow timeout in minutes (GitHub Actions standard field). Defaults to 20 minutes for agentic workflows. Has sensible defaults and can typically be omitted. Custom runners support longer timeouts beyond the GitHub-hosted runner limit. Supports GitHub Actions expressions (e.g. '${{ inputs.timeout }}') for reusable workflow_call workflows.", @@ -2686,7 +3158,11 @@ { "type": "integer", "minimum": 1, - "examples": [5, 10, 30] + "examples": [ + 5, + 10, + 30 + ] }, { "type": "string", @@ -2701,7 +3177,10 @@ { "type": "string", "description": "Simple concurrency group name to prevent multiple runs in the same group. Use expressions like '${{ github.workflow }}' for per-workflow isolation or '${{ github.ref }}' for per-branch isolation. Agentic workflows automatically generate enhanced concurrency policies using 'gh-aw-{engine-id}' as the default group to limit concurrent AI workloads across all workflows using the same engine.", - "examples": ["my-workflow-group", "workflow-${{ github.ref }}"] + "examples": [ + "my-workflow-group", + "workflow-${{ github.ref }}" + ] }, { "type": "object", @@ -2718,13 +3197,20 @@ }, "queue": { "type": "string", - "enum": ["single", "max"], + "enum": [ + "single", + "max" + ], "description": "Pending run queue behavior for this concurrency group. 'single' (default) allows one pending run and replaces older pending runs. 'max' allows up to 100 pending runs in FIFO order." }, "job-discriminator": { "type": "string", "description": "Additional discriminator expression appended to compiler-generated job-level concurrency groups (agent, output jobs). Use this when multiple workflow instances are dispatched concurrently with different inputs (fan-out pattern) to prevent job-level concurrency groups from colliding. For example, '${{ inputs.finding_id }}' ensures each dispatched run gets a unique job-level group. Supports GitHub Actions expressions. This field is stripped from the compiled lock file (it is a gh-aw extension, not a GitHub Actions field).", - "examples": ["${{ inputs.finding_id }}", "${{ inputs.item_id }}", "${{ github.run_id }}"] + "examples": [ + "${{ inputs.finding_id }}", + "${{ inputs.item_id }}", + "${{ github.run_id }}" + ] } }, "required": [], @@ -2776,7 +3262,9 @@ "inline-sub-agents": { "type": "boolean", "description": "Deprecated switch for inline sub-agent support. Inline sub-agents are enabled by default. Setting this to false is not supported and causes a compilation error.", - "examples": [true] + "examples": [ + true + ] }, "features": { "description": "Feature flags and configuration options for experimental or optional features in the workflow. Each feature can be a boolean flag or a string value. The 'action-tag' feature (string) specifies the tag or SHA to use when referencing actions/setup in compiled workflows (for testing purposes only).", @@ -2805,8 +3293,13 @@ }, "examples": [ { - "sonnet": ["mygateway/*sonnet-v3*"], - "": ["sonnet", "gpt-5-codex"] + "sonnet": [ + "mygateway/*sonnet-v3*" + ], + "": [ + "sonnet", + "gpt-5-codex" + ] } ] }, @@ -2837,7 +3330,9 @@ }, { "type": "object", - "required": ["variants"], + "required": [ + "variants" + ], "properties": { "variants": { "type": "array", @@ -2893,7 +3388,10 @@ "type": "array", "items": { "type": "object", - "required": ["name", "threshold"], + "required": [ + "name", + "threshold" + ], "properties": { "name": { "type": "string", @@ -2917,7 +3415,12 @@ }, "analysis_type": { "type": "string", - "enum": ["t_test", "mann_whitney", "proportion_test", "bayesian_ab"], + "enum": [ + "t_test", + "mann_whitney", + "proportion_test", + "bayesian_ab" + ], "description": "Statistical test to use for automated analysis by the reporting workflow. Valid values: t_test (Welch's two-sample t-test), mann_whitney (non-parametric rank test), proportion_test (two-proportion z-test), bayesian_ab (Bayesian A/B test)." }, "tags": { @@ -2952,15 +3455,24 @@ }, "examples": [ { - "feature1": ["A", "B"] + "feature1": [ + "A", + "B" + ] }, { "prompt_style": { - "variants": ["concise", "verbose"], + "variants": [ + "concise", + "verbose" + ], "description": "Test whether concise vs verbose prompts reduce token consumption", "hypothesis": "H0: no change in tokens. H1: concise reduces by >=15%", "metric": "effective_tokens", - "secondary_metrics": ["duration_ms", "discussion_word_count"], + "secondary_metrics": [ + "duration_ms", + "discussion_word_count" + ], "guardrail_metrics": [ { "name": "success_rate", @@ -2972,23 +3484,35 @@ } ], "min_samples": 25, - "weight": [50, 50], + "weight": [ + 50, + 50 + ], "issue": 1234, "start_date": "2026-05-01", "end_date": "2026-06-15", "analysis_type": "t_test", - "tags": ["cost", "prompting"], + "tags": [ + "cost", + "prompting" + ], "notify": { "issue": 1234 } }, - "model_temp": ["low", "high"] + "model_temp": [ + "low", + "high" + ] } ], "properties": { "storage": { "type": "string", - "enum": ["cache", "repo"], + "enum": [ + "cache", + "repo" + ], "default": "repo", "description": "Storage backend for experiment state. 'repo' (default) persists state to a git branch named 'experiments/{sanitizedWorkflowID}' (workflow ID lowercased with hyphens removed, e.g. 'my-workflow' -> 'experiments/myworkflow') for durability across cache evictions. 'cache' uses GitHub Actions cache (legacy behaviour). Repo storage is recommended because experiment data is valuable and more durable than cache." } @@ -2999,12 +3523,16 @@ "deprecated": true, "description": "DEPRECATED: Use 'disable-model-invocation' instead. Controls whether the custom agent should infer additional context from the conversation. This field is maintained for backward compatibility with existing custom agent files.", "x-deprecation-message": "'infer' is deprecated. Use 'disable-model-invocation' instead.", - "examples": [false] + "examples": [ + false + ] }, "disable-model-invocation": { "type": "boolean", "description": "Controls whether the custom agent should disable model invocation. When set to true, the agent will not make additional model calls. This is the preferred field name for custom agent files (replaces the deprecated 'infer' field).", - "examples": [true] + "examples": [ + true + ] }, "secrets": { "description": "Secret values passed to workflow execution. Secrets can be defined as simple strings (GitHub Actions expressions) or objects with 'value' and 'description' properties. Typically used to provide secrets to MCP servers or custom engines. Note: For passing secrets to reusable workflows, use the jobs..secrets field instead.", @@ -3018,7 +3546,9 @@ { "type": "object", "description": "Secret with metadata", - "required": ["value"], + "required": [ + "value" + ], "properties": { "value": { "type": "string", @@ -3070,7 +3600,9 @@ "description": "A deployment URL" } }, - "required": ["name"], + "required": [ + "name" + ], "additionalProperties": false } ] @@ -3138,7 +3670,9 @@ "description": "Additional Docker container options" } }, - "required": ["image"], + "required": [ + "image" + ], "additionalProperties": false } ] @@ -3208,7 +3742,9 @@ "description": "Additional Docker container options" } }, - "required": ["image"], + "required": [ + "image" + ], "additionalProperties": false } ] @@ -3220,16 +3756,26 @@ "examples": [ "defaults", { - "allowed": ["defaults", "github"] + "allowed": [ + "defaults", + "github" + ] }, { - "allowed": ["defaults", "python", "node", "*.example.com"] + "allowed": [ + "defaults", + "python", + "node", + "*.example.com" + ] } ], "oneOf": [ { "type": "string", - "enum": ["defaults"], + "enum": [ + "defaults" + ], "description": "Use default network permissions (basic infrastructure: certificates, JSON schema, Ubuntu, etc.)" }, { @@ -3268,7 +3814,10 @@ "oneOf": [ { "type": "string", - "enum": ["default", "awf"], + "enum": [ + "default", + "awf" + ], "description": "String format for sandbox type: 'default' for no sandbox, 'awf' for Agent Workflow Firewall. Note: Legacy 'srt' and 'sandbox-runtime' values are automatically migrated to 'awf'" }, { @@ -3277,7 +3826,10 @@ "properties": { "type": { "type": "string", - "enum": ["default", "awf"], + "enum": [ + "default", + "awf" + ], "description": "Legacy sandbox type field (use agent instead). Note: Legacy 'srt' and 'sandbox-runtime' values are automatically migrated to 'awf'" }, "agent": { @@ -3291,7 +3843,9 @@ }, { "type": "string", - "enum": ["awf"], + "enum": [ + "awf" + ], "description": "Sandbox type: 'awf' for Agent Workflow Firewall" }, { @@ -3300,12 +3854,16 @@ "properties": { "id": { "type": "string", - "enum": ["awf"], + "enum": [ + "awf" + ], "description": "Agent identifier (replaces 'type' field in new format): 'awf' for Agent Workflow Firewall" }, "type": { "type": "string", - "enum": ["awf"], + "enum": [ + "awf" + ], "description": "Legacy: Sandbox type to use (use 'id' instead)" }, "version": { @@ -3341,13 +3899,22 @@ "pattern": "^[^:]+:[^:]+:(ro|rw)$", "description": "Mount specification in format 'source:destination:mode'" }, - "examples": [["/host/data:/data:ro", "/usr/local/bin/custom-tool:/usr/local/bin/custom-tool:ro"]] + "examples": [ + [ + "/host/data:/data:ro", + "/usr/local/bin/custom-tool:/usr/local/bin/custom-tool:ro" + ] + ] }, "memory": { "type": "string", "description": "Memory limit for the AWF container (e.g., '4g', '8g'). Passed as --memory-limit to AWF. If not specified, AWF's default memory limit is used.", "pattern": "^[0-9]+(b|k|m|g|kb|mb|gb|B|K|M|G|KB|MB|GB)$", - "examples": ["4g", "8g", "512m"] + "examples": [ + "4g", + "8g", + "512m" + ] }, "config": { "type": "object", @@ -3463,16 +4030,26 @@ "description": "Container image for the MCP gateway executable (required)" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "x-internal": true, "description": "Optional version/tag for the container image (e.g., 'latest', 'v1.0.0')", - "examples": ["latest", "v1.0.0"] + "examples": [ + "latest", + "v1.0.0" + ] }, "entrypoint": { "type": "string", "x-internal": true, "description": "Optional custom entrypoint for the MCP gateway container. Overrides the container's default entrypoint.", - "examples": ["/bin/bash", "/custom/start.sh", "/usr/bin/env"] + "examples": [ + "/bin/bash", + "/custom/start.sh", + "/usr/bin/env" + ] }, "args": { "type": "array", @@ -3498,7 +4075,12 @@ "pattern": "^[^:]+:[^:]+:(ro|rw)$", "description": "Mount specification in format 'source:destination:mode'" }, - "examples": [["/host/data:/container/data:ro", "/host/config:/container/config:rw"]] + "examples": [ + [ + "/host/data:/container/data:ro", + "/host/config:/container/config:rw" + ] + ] }, "env": { "type": "object", @@ -3523,14 +4105,22 @@ }, "domain": { "type": "string", - "enum": ["localhost", "host.docker.internal"], + "enum": [ + "localhost", + "host.docker.internal" + ], "description": "Gateway domain for URL generation (default: 'host.docker.internal' when agent is enabled, 'localhost' when disabled)" }, "keepalive-interval": { "type": "integer", "description": "Keepalive ping interval in seconds for HTTP MCP backends. Sends periodic pings to prevent session expiry during long-running agent tasks. Set to -1 to disable keepalive pings. Unset or 0 uses the gateway default (1500 seconds = 25 minutes).", "minimum": -1, - "examples": [-1, 300, 600, 1500] + "examples": [ + -1, + 300, + 600, + 1500 + ] } }, "additionalProperties": false @@ -3563,7 +4153,10 @@ "if": { "type": "string", "description": "Conditional execution expression", - "examples": ["${{ github.event.workflow_run.event == 'workflow_dispatch' }}", "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}"] + "examples": [ + "${{ github.event.workflow_run.event == 'workflow_dispatch' }}", + "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}" + ] }, "steps": { "description": "Custom workflow steps", @@ -3744,7 +4337,9 @@ { "type": "integer", "not": { - "enum": [0] + "enum": [ + 0 + ] }, "description": "Maximum effective-token (ET) budget for AWF API proxy enforcement. Use a negative value to disable budget enforcement and token steering." }, @@ -3795,7 +4390,10 @@ "filesystem": { "type": "stdio", "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-filesystem"] + "args": [ + "-y", + "@modelcontextprotocol/server-filesystem" + ] } }, { @@ -3872,18 +4470,33 @@ }, "mode": { "type": "string", - "enum": ["gh-proxy", "local", "remote"], + "enum": [ + "gh-proxy", + "local", + "remote" + ], "description": "GitHub access mode. Prefer 'gh-proxy' for better performance (uses pre-authenticated gh CLI prompt guidance). Legacy MCP transport values 'local' and 'remote' are accepted for backward compatibility and use GitHub MCP server prompt guidance." }, "type": { "type": "string", - "enum": ["local", "remote"], + "enum": [ + "local", + "remote" + ], "description": "GitHub MCP transport type: 'local' (Docker-based, default) or 'remote' (hosted at api.githubcopilot.com)" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version specification for the GitHub MCP server (used with 'local' type). Can be a string (e.g., 'v1.0.0', 'latest') or number (e.g., 20, 3.11). Numeric values are automatically converted to strings at runtime.", - "examples": ["v1.0.0", "latest", 20, 3.11] + "examples": [ + "v1.0.0", + "latest", + 20, + 3.11 + ] }, "args": { "type": "array", @@ -3937,14 +4550,26 @@ "pattern": "^[^:]+:[^:]+(:(ro|rw))?$", "description": "Mount specification in format 'host:container:mode'" }, - "examples": [["/data:/data:ro", "/tmp:/tmp:rw"], ["/opt:/opt:ro"]] + "examples": [ + [ + "/data:/data:ro", + "/tmp:/tmp:rw" + ], + [ + "/opt:/opt:ro" + ] + ] }, "allowed-repos": { "description": "Guard policy: repository access configuration. Restricts which repositories the agent can access. Use 'all' to allow all repos, 'public' for public repositories only, '${{ github.repository }}' for the current repository, or an array of repository patterns (e.g., 'owner/repo', 'owner/*', 'owner/prefix*').", "oneOf": [ { "type": "string", - "enum": ["all", "public", "${{ github.repository }}"], + "enum": [ + "all", + "public", + "${{ github.repository }}" + ], "description": "Allow access to all repositories ('all'), only public repositories ('public'), or the current repository ('${{ github.repository }}')" }, { @@ -3965,7 +4590,11 @@ "oneOf": [ { "type": "string", - "enum": ["all", "public", "${{ github.repository }}"], + "enum": [ + "all", + "public", + "${{ github.repository }}" + ], "description": "Allow access to all repositories ('all'), only public repositories ('public'), or the current repository ('${{ github.repository }}')" }, { @@ -3982,7 +4611,12 @@ "min-integrity": { "type": "string", "description": "Guard policy: minimum required integrity level for repository access. Restricts the agent to users with at least the specified permission level.", - "enum": ["none", "unapproved", "approved", "merged"] + "enum": [ + "none", + "unapproved", + "approved", + "merged" + ] }, "blocked-users": { "description": "Guard policy: GitHub usernames whose content is unconditionally blocked. Items from these users receive 'blocked' integrity (below 'none') and are always denied, even when 'min-integrity' is 'none'. Cannot be overridden by 'approval-labels'. Requires 'min-integrity' to be set. Accepts an array of usernames, a comma-separated string, a newline-separated string, or a GitHub Actions expression (e.g. '${{ vars.BLOCKED_USERS }}').", @@ -4044,10 +4678,27 @@ "items": { "type": "string", "description": "GitHub ReactionContent enum value", - "enum": ["THUMBS_UP", "THUMBS_DOWN", "HEART", "HOORAY", "CONFUSED", "ROCKET", "EYES", "LAUGH"] + "enum": [ + "THUMBS_UP", + "THUMBS_DOWN", + "HEART", + "HOORAY", + "CONFUSED", + "ROCKET", + "EYES", + "LAUGH" + ] }, - "default": ["THUMBS_UP", "HEART"], - "examples": [["THUMBS_UP", "HEART"]] + "default": [ + "THUMBS_UP", + "HEART" + ], + "examples": [ + [ + "THUMBS_UP", + "HEART" + ] + ] }, "disapproval-reactions": { "type": "array", @@ -4055,21 +4706,47 @@ "items": { "type": "string", "description": "GitHub ReactionContent enum value", - "enum": ["THUMBS_UP", "THUMBS_DOWN", "HEART", "HOORAY", "CONFUSED", "ROCKET", "EYES", "LAUGH"] + "enum": [ + "THUMBS_UP", + "THUMBS_DOWN", + "HEART", + "HOORAY", + "CONFUSED", + "ROCKET", + "EYES", + "LAUGH" + ] }, - "default": ["THUMBS_DOWN", "CONFUSED"], - "examples": [["THUMBS_DOWN", "CONFUSED"]] + "default": [ + "THUMBS_DOWN", + "CONFUSED" + ], + "examples": [ + [ + "THUMBS_DOWN", + "CONFUSED" + ] + ] }, "disapproval-integrity": { "type": "string", "description": "Guard policy: integrity level assigned when a disapproval reaction is present. Optional, defaults to 'none'. Requires the 'integrity-reactions' feature flag and MCPG >= v0.2.18.", - "enum": ["none", "unapproved", "approved", "merged"], + "enum": [ + "none", + "unapproved", + "approved", + "merged" + ], "default": "none" }, "endorser-min-integrity": { "type": "string", "description": "Guard policy: minimum integrity level required for an endorser (reactor) to promote content. Optional, defaults to 'approved'. Requires the 'integrity-reactions' feature flag and MCPG >= v0.2.18.", - "enum": ["unapproved", "approved", "merged"], + "enum": [ + "unapproved", + "approved", + "merged" + ], "default": "approved" }, "github-app": { @@ -4080,16 +4757,30 @@ "additionalProperties": false, "examples": [ { - "toolsets": ["pull_requests", "actions", "repos"] + "toolsets": [ + "pull_requests", + "actions", + "repos" + ] }, { - "allowed": ["search_pull_requests", "pull_request_read", "list_pull_requests", "get_file_contents", "list_commits", "get_commit"] + "allowed": [ + "search_pull_requests", + "pull_request_read", + "list_pull_requests", + "get_file_contents", + "list_commits", + "get_commit" + ] }, { "read-only": true }, { - "toolsets": ["pull_requests", "repos"] + "toolsets": [ + "pull_requests", + "repos" + ] } ] } @@ -4097,14 +4788,25 @@ "examples": [ null, { - "toolsets": ["pull_requests", "actions", "repos"] + "toolsets": [ + "pull_requests", + "actions", + "repos" + ] }, { - "allowed": ["search_pull_requests", "pull_request_read", "get_file_contents"] + "allowed": [ + "search_pull_requests", + "pull_request_read", + "get_file_contents" + ] }, { "read-only": true, - "toolsets": ["repos", "issues"] + "toolsets": [ + "repos", + "issues" + ] }, false ] @@ -4131,10 +4833,36 @@ ], "examples": [ true, - ["git fetch", "git checkout", "git status", "git diff", "git log", "make recompile", "make fmt", "make lint", "make test-unit", "cat", "echo", "ls"], - ["echo", "ls", "cat"], - ["gh pr list *", "gh search prs *", "jq *"], - ["date *", "echo *", "cat", "ls"] + [ + "git fetch", + "git checkout", + "git status", + "git diff", + "git log", + "make recompile", + "make fmt", + "make lint", + "make test-unit", + "cat", + "echo", + "ls" + ], + [ + "echo", + "ls", + "cat" + ], + [ + "gh pr list *", + "gh search prs *", + "jq *" + ], + [ + "date *", + "echo *", + "cat", + "ls" + ] ] }, "web-fetch": { @@ -4211,9 +4939,15 @@ "description": "Playwright tool configuration with custom version and arguments", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version pin. In CLI mode (recommended): the @playwright/cli npm package version (e.g., '0.1.11'). In MCP mode (deprecated): the Playwright browser Docker image version (e.g., 'v1.56.1'). Omit to use the default version.", - "examples": ["0.1.11", "v1.56.1"] + "examples": [ + "0.1.11", + "v1.56.1" + ] }, "args": { "type": "array", @@ -4225,7 +4959,10 @@ "mode": { "type": "string", "description": "Integration mode: 'cli' (recommended) installs @playwright/cli via npm for token-efficient CLI invocations \u2014 use playwright-cli commands in bash and localhost to reach local servers; 'mcp' (deprecated) runs a Docker-based MCP server.", - "enum": ["cli", "mcp"] + "enum": [ + "cli", + "mcp" + ] } }, "additionalProperties": false @@ -4244,7 +4981,10 @@ "description": "Enable agentic-workflows tool with default settings (same as true)" } ], - "examples": [true, null] + "examples": [ + true, + null + ] }, "cache-memory": { "description": "Cache memory MCP configuration for persistent memory storage", @@ -4281,7 +5021,10 @@ }, "scope": { "type": "string", - "enum": ["workflow", "repo"], + "enum": [ + "workflow", + "repo" + ], "default": "workflow", "description": "Cache restore key scope: 'workflow' (default, only restores from same workflow) or 'repo' (restores from any workflow in the repository). Use 'repo' with caution as it allows cross-workflow cache sharing." }, @@ -4334,7 +5077,10 @@ }, "scope": { "type": "string", - "enum": ["workflow", "repo"], + "enum": [ + "workflow", + "repo" + ], "default": "workflow", "description": "Cache restore key scope: 'workflow' (default, only restores from same workflow) or 'repo' (restores from any workflow in the repository). Use 'repo' with caution as it allows cross-workflow cache sharing." }, @@ -4346,7 +5092,10 @@ "description": "List of allowed file extensions (e.g., [\".json\", \".txt\"]). Default: [\".json\", \".jsonl\", \".txt\", \".md\", \".csv\"]" } }, - "required": ["id", "key"], + "required": [ + "id", + "key" + ], "additionalProperties": false }, "minItems": 1, @@ -4437,7 +5186,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -4458,7 +5210,11 @@ { "type": "integer", "minimum": 1, - "examples": [60, 120, 300] + "examples": [ + 60, + 120, + 300 + ] }, { "type": "string", @@ -4473,7 +5229,11 @@ { "type": "integer", "minimum": 1, - "examples": [30, 60, 120] + "examples": [ + 30, + 60, + 120 + ] }, { "type": "string", @@ -4485,7 +5245,9 @@ "cli-proxy": { "type": "boolean", "description": "When true, each user-facing MCP server is mounted as a standalone CLI tool on PATH. The agent can then call MCP servers via shell commands (e.g. 'github issue_read --method get ...'). CLI-mounted servers remain in the MCP gateway config so their containers can start, and are removed only from the agent's final config during convert_gateway_config_*.sh processing. Default: false.", - "examples": [true] + "examples": [ + true + ] }, "serena": { "description": "REMOVED: Built-in support for Serena has been removed. Use the shared/mcp/serena.md workflow instead.", @@ -4769,17 +5531,25 @@ "description": "Optional custom name for the cache step (overrides auto-generated name)" } }, - "required": ["key", "path"], + "required": [ + "key", + "path" + ], "additionalProperties": false, "examples": [ { "key": "node-modules-${{ hashFiles('package-lock.json') }}", "path": "node_modules", - "restore-keys": ["node-modules-"] + "restore-keys": [ + "node-modules-" + ] }, { "key": "build-cache-${{ github.sha }}", - "path": ["dist", ".cache"], + "path": [ + "dist", + ".cache" + ], "restore-keys": "build-cache-", "fail-on-cache-miss": false } @@ -4844,7 +5614,10 @@ "description": "Optional custom name for the cache step (overrides auto-generated name)" } }, - "required": ["key", "path"], + "required": [ + "key", + "path" + ], "additionalProperties": false } } @@ -4858,13 +5631,18 @@ { "create-issue": { "title-prefix": "[AI] ", - "labels": ["automation", "ai-generated"] + "labels": [ + "automation", + "ai-generated" + ] } }, { "create-pull-request": { "title-prefix": "[Bot] ", - "labels": ["bot"] + "labels": [ + "bot" + ] } }, { @@ -4886,7 +5664,23 @@ "items": { "type": "string" }, - "examples": [["repo"], ["repo", "octocat/hello-world"], ["microsoft/vscode", "microsoft/typescript"], ["repo", "${{ github.repository }}"]] + "examples": [ + [ + "repo" + ], + [ + "repo", + "octocat/hello-world" + ], + [ + "microsoft/vscode", + "microsoft/typescript" + ], + [ + "repo", + "${{ github.repository }}" + ] + ] }, "create-issue": { "oneOf": [ @@ -4988,7 +5782,9 @@ }, { "type": "boolean", - "enum": [false], + "enum": [ + false + ], "description": "Set to false to explicitly disable expiration" } ], @@ -5027,28 +5823,43 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, "examples": [ { "title-prefix": "[ca] ", - "labels": ["automation", "dependencies"], + "labels": [ + "automation", + "dependencies" + ], "assignees": "copilot" }, { "title-prefix": "[duplicate-code] ", - "labels": ["code-quality", "automated-analysis"], + "labels": [ + "code-quality", + "automated-analysis" + ], "assignees": "copilot" }, { - "allowed-repos": ["org/other-repo", "org/another-repo"], + "allowed-repos": [ + "org/other-repo", + "org/another-repo" + ], "title-prefix": "[cross-repo] " }, { "title-prefix": "[weekly-report] ", - "labels": ["report", "automation"], + "labels": [ + "report", + "automation" + ], "close-older-issues": true } ] @@ -5105,7 +5916,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -5160,7 +5974,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -5177,7 +5994,9 @@ { "type": "object", "description": "Configuration for managing GitHub Projects boards. Enable agents to add issues and pull requests to projects, update custom field values (status, priority, effort, dates), create project fields and views. By default it is update-only: if the project does not exist, the job fails with instructions to create it. To allow workflows to create missing projects, explicitly opt in via agent output field create_if_missing=true. Requires a Personal Access Token (PAT) or GitHub App token with Projects permissions (default GITHUB_TOKEN cannot be used). Agent output includes: project (full URL or temporary project ID like aw_XXXXXXXXXXXX or #aw_XXXXXXXXXXXX from create_project), content_type (issue|pull_request|draft_issue), content_number, fields, create_if_missing. For specialized operations, agent can also provide: operation (create_fields|create_view), field_definitions (array of field configs when operation=create_fields), view (view config object when operation=create_view).", - "required": ["project"], + "required": [ + "project" + ], "properties": { "max": { "description": "Maximum number of project operations to perform (default: 10). Each operation may add a project item, or update its fields. Supports integer or GitHub Actions expression (e.g. '${{ inputs.max }}').", @@ -5202,7 +6021,10 @@ "type": "string", "description": "Target project URL for update-project operations. This is required in the configuration for documentation purposes. Agent messages MUST explicitly include the project field in their output - the configured value is not used as a fallback. Must be a valid GitHub Projects v2 URL.", "pattern": "^https://github\\.com/(users|orgs)/([^/]+|<[A-Z_]+>)/projects/(\\d+|<[A-Z_]+>)$", - "examples": ["https://github.com/orgs/myorg/projects/123", "https://github.com/users/username/projects/456"] + "examples": [ + "https://github.com/orgs/myorg/projects/123", + "https://github.com/users/username/projects/456" + ] }, "target-repo": { "description": "Default repository in format 'owner/repo' for cross-repository content resolution. When specified, the agent can use 'target_repo' in agent output to resolve issues or PRs from this repository. Wildcards ('*') are not allowed. Supports GitHub Actions expression syntax (e.g., '${{ vars.TARGET_REPO }}').", @@ -5241,7 +6063,10 @@ "items": { "type": "object", "description": "View configuration for creating project views", - "required": ["name", "layout"], + "required": [ + "name", + "layout" + ], "properties": { "name": { "type": "string", @@ -5249,7 +6074,11 @@ }, "layout": { "type": "string", - "enum": ["table", "board", "roadmap"], + "enum": [ + "table", + "board", + "roadmap" + ], "description": "The layout type of the view" }, "filter": { @@ -5276,7 +6105,10 @@ "description": "Optional array of project custom fields to create up-front.", "items": { "type": "object", - "required": ["name", "data-type"], + "required": [ + "name", + "data-type" + ], "properties": { "name": { "type": "string", @@ -5284,7 +6116,13 @@ }, "data-type": { "type": "string", - "enum": ["DATE", "TEXT", "NUMBER", "SINGLE_SELECT", "ITERATION"], + "enum": [ + "DATE", + "TEXT", + "NUMBER", + "SINGLE_SELECT", + "ITERATION" + ], "description": "The GitHub Projects v2 custom field type" }, "options": { @@ -5301,7 +6139,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -5361,7 +6202,10 @@ "items": { "type": "object", "description": "View configuration for creating project views", - "required": ["name", "layout"], + "required": [ + "name", + "layout" + ], "properties": { "name": { "type": "string", @@ -5369,7 +6213,11 @@ }, "layout": { "type": "string", - "enum": ["table", "board", "roadmap"], + "enum": [ + "table", + "board", + "roadmap" + ], "description": "The layout type of the view" }, "filter": { @@ -5396,7 +6244,10 @@ "description": "Optional array of project custom fields to create automatically after project creation.", "items": { "type": "object", - "required": ["name", "data-type"], + "required": [ + "name", + "data-type" + ], "properties": { "name": { "type": "string", @@ -5404,7 +6255,13 @@ }, "data-type": { "type": "string", - "enum": ["DATE", "TEXT", "NUMBER", "SINGLE_SELECT", "ITERATION"], + "enum": [ + "DATE", + "TEXT", + "NUMBER", + "SINGLE_SELECT", + "ITERATION" + ], "description": "The GitHub Projects v2 custom field type" }, "options": { @@ -5421,7 +6278,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -5441,7 +6301,9 @@ { "type": "object", "description": "Configuration for posting status updates to GitHub Projects. Status updates provide stakeholder communication about project progress, health, and timeline. Each update appears in the project's Updates tab and creates a historical record. Requires a Personal Access Token (PAT) or GitHub App token with Projects read & write permission (default GITHUB_TOKEN cannot be used). Typically used by scheduled workflows or orchestrators to post regular progress summaries with status indicators (on-track, at-risk, off-track, complete, inactive), dates, and progress details.", - "required": ["project"], + "required": [ + "project" + ], "properties": { "max": { "description": "Maximum number of status updates to create (default: 1). Typically 1 per orchestrator run. Supports integer or GitHub Actions expression (e.g. '${{ inputs.max }}').", @@ -5466,12 +6328,18 @@ "type": "string", "description": "Target project URL for status update operations. This is required in the configuration for documentation purposes. Agent messages MUST explicitly include the project field in their output - the configured value is not used as a fallback. Must be a valid GitHub Projects v2 URL.", "pattern": "^https://github\\.com/(users|orgs)/([^/]+|<[A-Z_]+>)/projects/(\\d+|<[A-Z_]+>)$", - "examples": ["https://github.com/orgs/myorg/projects/123", "https://github.com/users/username/projects/456"] + "examples": [ + "https://github.com/orgs/myorg/projects/123", + "https://github.com/users/username/projects/456" + ] }, "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -5503,9 +6371,16 @@ "description": "Optional prefix for the discussion title" }, "category": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional discussion category. Can be a category ID (string or numeric value), category name, or category slug/route. If not specified, uses the first available category. Matched first against category IDs, then against category names, then against category slugs. Numeric values are automatically converted to strings at runtime.", - "examples": ["General", "audits", 123456789] + "examples": [ + "General", + "audits", + 123456789 + ] }, "min-body-length": { "type": "integer", @@ -5587,7 +6462,9 @@ }, { "type": "boolean", - "enum": [false], + "enum": [ + false + ], "description": "Set to false to explicitly disable expiration" } ], @@ -5601,7 +6478,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -5623,12 +6503,17 @@ "close-older-discussions": true }, { - "labels": ["weekly-report", "automation"], + "labels": [ + "weekly-report", + "automation" + ], "category": "reports", "close-older-discussions": true }, { - "allowed-repos": ["org/other-repo"], + "allowed-repos": [ + "org/other-repo" + ], "category": "General" } ] @@ -5693,7 +6578,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -5702,7 +6590,10 @@ "required-category": "Ideas" }, { - "required-labels": ["resolved", "completed"], + "required-labels": [ + "resolved", + "completed" + ], "max": 1 } ] @@ -5770,7 +6661,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "github-token": { "$ref": "#/$defs/github_token", @@ -5842,11 +6736,18 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "state-reason": { "type": "string", - "enum": ["completed", "not_planned", "duplicate"], + "enum": [ + "completed", + "not_planned", + "duplicate" + ], "default": "completed", "description": "Reason for closing the issue (default: completed)" } @@ -5857,7 +6758,10 @@ "required-title-prefix": "[refactor] " }, { - "required-labels": ["automated", "stale"], + "required-labels": [ + "automated", + "stale" + ], "max": 10 } ] @@ -5922,7 +6826,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -5931,7 +6838,10 @@ "required-title-prefix": "[bot] " }, { - "required-labels": ["automated", "outdated"], + "required-labels": [ + "automated", + "outdated" + ], "max": 5 } ] @@ -5996,7 +6906,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -6005,7 +6918,10 @@ "required-title-prefix": "[bot] " }, { - "required-labels": ["automated", "ready"], + "required-labels": [ + "automated", + "ready" + ], "max": 1 } ] @@ -6072,7 +6988,14 @@ "description": "List of allowed reasons for hiding older comments when hide-older-comments is enabled. Default: all reasons allowed (spam, abuse, off_topic, outdated, resolved, low_quality).", "items": { "type": "string", - "enum": ["spam", "abuse", "off_topic", "outdated", "resolved", "low_quality"] + "enum": [ + "spam", + "abuse", + "off_topic", + "outdated", + "resolved", + "low_quality" + ] } }, "discussions": { @@ -6112,7 +7035,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, @@ -6253,7 +7179,11 @@ }, "if-no-changes": { "type": "string", - "enum": ["warn", "error", "ignore"], + "enum": [ + "warn", + "error", + "ignore" + ], "description": "Behavior when no changes to push: 'warn' (default - log warning but succeed), 'error' (fail the action), or 'ignore' (silent success)" }, "allow-empty": { @@ -6381,7 +7311,11 @@ "oneOf": [ { "type": "string", - "enum": ["blocked", "allowed", "fallback-to-issue"], + "enum": [ + "blocked", + "allowed", + "fallback-to-issue" + ], "description": "Controls protected-file protection. blocked (default): hard-block any patch that modifies package manifests (e.g. package.json, go.mod), engine instruction files (e.g. AGENTS.md, CLAUDE.md) or .github/ files. allowed: allow all changes. fallback-to-issue: push the branch but create a review issue instead of a PR, so a human can review the manifest changes before merging.", "default": "blocked" }, @@ -6397,7 +7331,11 @@ "oneOf": [ { "type": "string", - "enum": ["blocked", "allowed", "fallback-to-issue"], + "enum": [ + "blocked", + "allowed", + "fallback-to-issue" + ], "description": "Protection policy. blocked (default): hard-block any patch that modifies protected files. allowed: allow all changes. fallback-to-issue: push the branch but create a review issue instead of a PR.", "default": "blocked" }, @@ -6414,7 +7352,15 @@ "type": "string" }, "description": "List of filenames or path prefixes to remove from the default protected-file set. Items are matched by basename (e.g. \"AGENTS.md\") or path prefix (e.g. \".agents/\"). Use this to allow the agent to modify specific files that are otherwise blocked by default.", - "examples": [["AGENTS.md"], ["AGENTS.md", ".agents/"]] + "examples": [ + [ + "AGENTS.md" + ], + [ + "AGENTS.md", + ".agents/" + ] + ] } }, "additionalProperties": false, @@ -6451,7 +7397,10 @@ "oneOf": [ { "type": "string", - "enum": ["am", "bundle"], + "enum": [ + "am", + "bundle" + ], "default": "bundle", "description": "Transport format for packaging changes. \"bundle\" (default) uses git bundle, which preserves merge commit topology, per-commit authorship, and merge-resolution-only content. \"am\" uses git format-patch/git am." }, @@ -6471,26 +7420,38 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "allow-workflows": { "type": "boolean", "description": "When true, adds workflows: write to the GitHub App token permissions. Required when allowed-files targets .github/workflows/ paths. Requires safe-outputs.github-app to be configured because the workflows permission is a GitHub App-only permission and cannot be granted via GITHUB_TOKEN.", "default": false, - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false, "examples": [ { "title-prefix": "[docs] ", - "labels": ["documentation", "automation"], + "labels": [ + "documentation", + "automation" + ], "reviewers": "copilot", "draft": false }, { "title-prefix": "[security-fix] ", - "labels": ["security", "automated-fix"], + "labels": [ + "security", + "automated-fix" + ], "reviewers": "copilot" } ] @@ -6526,7 +7487,10 @@ "side": { "type": "string", "description": "Side of the diff for comments: 'LEFT' or 'RIGHT' (default: 'RIGHT')", - "enum": ["LEFT", "RIGHT"] + "enum": [ + "LEFT", + "RIGHT" + ] }, "target": { "type": "string", @@ -6550,7 +7514,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -6602,7 +7569,11 @@ }, { "type": "string", - "enum": ["always", "none", "if-body"], + "enum": [ + "always", + "none", + "if-body" + ], "description": "Controls when AI-generated footer is added to the review body: 'always' (default), 'none' (never), or 'if-body' (only when review has body text)." } ], @@ -6627,7 +7598,11 @@ "type": "array", "items": { "type": "string", - "enum": ["APPROVE", "COMMENT", "REQUEST_CHANGES"] + "enum": [ + "APPROVE", + "COMMENT", + "REQUEST_CHANGES" + ] }, "description": "Optional list of allowed review event types. If omitted, all event types (APPROVE, COMMENT, REQUEST_CHANGES) are allowed. Use this to restrict the agent to specific event types, e.g. [COMMENT, REQUEST_CHANGES] to prevent approvals.", "minItems": 1 @@ -6643,7 +7618,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -6714,7 +7692,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -6765,7 +7746,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -6830,7 +7814,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -6869,7 +7856,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -6958,7 +7948,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -7043,7 +8036,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -7144,7 +8140,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -7207,7 +8206,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -7274,7 +8276,10 @@ ] }, "target": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Target issue/PR to assign agents to. Use 'triggering' (default) for the triggering issue/PR, '*' to require explicit issue_number/pull_number, or a specific issue/PR number. With 'triggering', auto-resolves from github.event.issue.number or github.event.pull_request.number." }, "target-repo": { @@ -7308,7 +8313,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -7355,7 +8363,10 @@ ] }, "target": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Target issue to assign users to. Use 'triggering' (default) for the triggering issue, '*' to allow any issue, or a specific issue number." }, "target-repo": { @@ -7381,7 +8392,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -7439,7 +8453,10 @@ ] }, "target": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Target issue to unassign users from. Use 'triggering' (default) for the triggering issue, '*' to allow any issue, or a specific issue number." }, "target-repo": { @@ -7460,7 +8477,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -7541,7 +8561,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -7568,7 +8591,10 @@ "description": "Allow updating issue title - presence of key indicates field can be updated" }, "body": { - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "description": "Allow updating issue body. Set to true to enable body updates, false to disable. For backward compatibility, null (body:) also enables body updates.", "default": true }, @@ -7614,7 +8640,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -7662,7 +8691,11 @@ "operation": { "type": "string", "description": "Default operation for body updates: 'append' (add to end), 'prepend' (add to start), or 'replace' (overwrite completely). Defaults to 'replace' if not specified.", - "enum": ["append", "prepend", "replace"] + "enum": [ + "append", + "prepend", + "replace" + ] }, "footer": { "type": "boolean", @@ -7695,7 +8728,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -7774,7 +8810,10 @@ "staged": { "type": "boolean", "description": "If true, evaluate merge gates and emit preview results without executing the merge API call.", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-title-prefix": { "type": "string", @@ -7868,7 +8907,11 @@ }, "if-no-changes": { "type": "string", - "enum": ["warn", "error", "ignore"], + "enum": [ + "warn", + "error", + "ignore" + ], "description": "Behavior when no changes to push: 'warn' (default - log warning but succeed), 'error' (fail the action), or 'ignore' (silent success)" }, "ignore-missing-branch-failure": { @@ -7893,7 +8936,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "github-token-for-extra-empty-commit": { "type": "string", @@ -7934,7 +8980,11 @@ "oneOf": [ { "type": "string", - "enum": ["blocked", "allowed", "fallback-to-issue"], + "enum": [ + "blocked", + "allowed", + "fallback-to-issue" + ], "description": "Controls protected-file protection. blocked (default): hard-block any patch that modifies package manifests (e.g. package.json, go.mod), engine instruction files (e.g. AGENTS.md, CLAUDE.md) or .github/ files. allowed: allow all changes. fallback-to-issue: create a review issue instead of pushing to the PR branch, so a human can review the changes before applying.", "default": "blocked" }, @@ -7950,7 +9000,11 @@ "oneOf": [ { "type": "string", - "enum": ["blocked", "allowed", "fallback-to-issue"], + "enum": [ + "blocked", + "allowed", + "fallback-to-issue" + ], "description": "Protection policy. blocked (default): hard-block any patch that modifies protected files. allowed: allow all changes. fallback-to-issue: create a review issue instead of pushing.", "default": "blocked" }, @@ -7967,7 +9021,15 @@ "type": "string" }, "description": "List of filenames or path prefixes to remove from the default protected-file set. Items are matched by basename (e.g. \"AGENTS.md\") or path prefix (e.g. \".agents/\"). Use this to allow the agent to modify specific files that are otherwise blocked by default.", - "examples": [["AGENTS.md"], ["AGENTS.md", ".agents/"]] + "examples": [ + [ + "AGENTS.md" + ], + [ + "AGENTS.md", + ".agents/" + ] + ] } }, "additionalProperties": false, @@ -7994,7 +9056,10 @@ "oneOf": [ { "type": "string", - "enum": ["am", "bundle"], + "enum": [ + "am", + "bundle" + ], "default": "bundle", "description": "Transport format for packaging changes. \"bundle\" (default) uses git bundle, which preserves merge commit topology, per-commit authorship, and merge-resolution-only content. \"am\" uses git format-patch/git am." }, @@ -8010,7 +9075,10 @@ "type": "boolean", "description": "When true, adds workflows: write to the GitHub App token permissions. Required when allowed-files targets .github/workflows/ paths. Requires safe-outputs.github-app to be configured because the workflows permission is a GitHub App-only permission and cannot be granted via GITHUB_TOKEN.", "default": false, - "examples": [true, false] + "examples": [ + true, + false + ] }, "check-branch-protection": { "type": "boolean", @@ -8057,7 +9125,14 @@ "description": "List of allowed reasons for hiding comments. Default: all reasons allowed (spam, abuse, off_topic, outdated, resolved, low_quality).", "items": { "type": "string", - "enum": ["spam", "abuse", "off_topic", "outdated", "resolved", "low_quality"] + "enum": [ + "spam", + "abuse", + "off_topic", + "outdated", + "resolved", + "low_quality" + ] } }, "discussions": { @@ -8067,7 +9142,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -8141,7 +9219,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -8213,7 +9294,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "required-labels": { "type": "array", @@ -8279,10 +9363,15 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, - "required": ["workflows"], + "required": [ + "workflows" + ], "additionalProperties": false }, { @@ -8341,7 +9430,13 @@ "properties": { "type": { "type": "string", - "enum": ["string", "number", "boolean", "choice", "environment"], + "enum": [ + "string", + "number", + "boolean", + "choice", + "environment" + ], "description": "Input type" }, "description": { @@ -8388,10 +9483,16 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, - "required": ["workflow", "event_type"], + "required": [ + "workflow", + "event_type" + ], "additionalProperties": false } }, @@ -8434,10 +9535,15 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, - "required": ["workflows"], + "required": [ + "workflows" + ], "additionalProperties": false }, { @@ -8498,7 +9604,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -8560,7 +9669,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -8610,7 +9722,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -8675,7 +9790,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -8767,7 +9885,10 @@ "if-no-files": { "type": "string", "description": "Behaviour when no files match: 'error' (default) or 'ignore'", - "enum": ["error", "ignore"], + "enum": [ + "error", + "ignore" + ], "default": "error" } }, @@ -8780,7 +9901,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub Actions artifact uploads (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -8827,7 +9951,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -8842,7 +9969,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] }, "env": { "type": "object", @@ -8858,7 +9988,11 @@ "github-token": { "$ref": "#/$defs/github_token", "description": "GitHub token to use for safe output jobs. Typically a secret reference like ${{ secrets.GITHUB_TOKEN }} or ${{ secrets.CUSTOM_PAT }}", - "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}"] + "examples": [ + "${{ secrets.GITHUB_TOKEN }}", + "${{ secrets.CUSTOM_PAT }}", + "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}" + ] }, "github-app": { "$ref": "#/$defs/github_app", @@ -9044,7 +10178,13 @@ }, "type": { "type": "string", - "enum": ["string", "boolean", "choice", "number", "environment"], + "enum": [ + "string", + "boolean", + "choice", + "number", + "environment" + ], "description": "Input parameter type. Supports: string (default), boolean, choice (string with predefined options), number, and environment (string referencing a GitHub environment)", "default": "string" }, @@ -9140,7 +10280,11 @@ }, "type": { "type": "string", - "enum": ["string", "boolean", "number"], + "enum": [ + "string", + "boolean", + "number" + ], "description": "Input parameter type", "default": "string" } @@ -9155,7 +10299,9 @@ "description": "JavaScript handler body. Write only the code that runs inside the handler for each item \u2014 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." } }, - "required": ["script"], + "required": [ + "script" + ], "additionalProperties": false } }, @@ -9168,82 +10314,129 @@ "footer": { "type": "string", "description": "Custom footer message template for AI-generated content. Available placeholders: {workflow_name}, {run_url}, {triggering_number}, {workflow_source}, {workflow_source_url}. Example: '> Generated by [{workflow_name}]({run_url})'", - "examples": ["> Generated by [{workflow_name}]({run_url})", "> AI output from [{workflow_name}]({run_url}) for #{triggering_number}"] + "examples": [ + "> Generated by [{workflow_name}]({run_url})", + "> AI output from [{workflow_name}]({run_url}) for #{triggering_number}" + ] }, "footer-install": { "type": "string", "description": "Custom installation instructions template appended to the footer. Available placeholders: {workflow_source}, {workflow_source_url}. Example: '> Install: `gh aw add {workflow_source}`'", - "examples": ["> Install: `gh aw add {workflow_source}`", "> [Add this workflow]({workflow_source_url})"] + "examples": [ + "> Install: `gh aw add {workflow_source}`", + "> [Add this workflow]({workflow_source_url})" + ] }, "footer-workflow-recompile": { "type": "string", "description": "Custom footer message template for workflow recompile issues. Available placeholders: {workflow_name}, {run_url}, {repository}. Example: '> Workflow sync report by [{workflow_name}]({run_url}) for {repository}'", - "examples": ["> Workflow sync report by [{workflow_name}]({run_url}) for {repository}", "> Maintenance report by [{workflow_name}]({run_url})"] + "examples": [ + "> Workflow sync report by [{workflow_name}]({run_url}) for {repository}", + "> Maintenance report by [{workflow_name}]({run_url})" + ] }, "footer-workflow-recompile-comment": { "type": "string", "description": "Custom footer message template for comments on workflow recompile issues. Available placeholders: {workflow_name}, {run_url}, {repository}. Example: '> Update from [{workflow_name}]({run_url}) for {repository}'", - "examples": ["> Update from [{workflow_name}]({run_url}) for {repository}", "> Maintenance update by [{workflow_name}]({run_url})"] + "examples": [ + "> Update from [{workflow_name}]({run_url}) for {repository}", + "> Maintenance update by [{workflow_name}]({run_url})" + ] }, "staged-title": { "type": "string", "description": "Custom title template for staged mode preview. Available placeholders: {operation}. Example: '\ud83c\udfad Preview: {operation}'", - "examples": ["\ud83c\udfad Preview: {operation}", "## Staged Mode: {operation}"] + "examples": [ + "\ud83c\udfad Preview: {operation}", + "## Staged Mode: {operation}" + ] }, "staged-description": { "type": "string", "description": "Custom description template for staged mode preview. Available placeholders: {operation}. Example: 'The following {operation} would occur if staged mode was disabled:'", - "examples": ["The following {operation} would occur if staged mode was disabled:"] + "examples": [ + "The following {operation} would occur if staged mode was disabled:" + ] }, "run-started": { "type": "string", "description": "Custom message template for workflow activation comment. Available placeholders: {workflow_name}, {run_url}, {event_type}. Default: 'Agentic [{workflow_name}]({run_url}) triggered by this {event_type}.'", - "examples": ["Agentic [{workflow_name}]({run_url}) triggered by this {event_type}.", "[{workflow_name}]({run_url}) started processing this {event_type}."] + "examples": [ + "Agentic [{workflow_name}]({run_url}) triggered by this {event_type}.", + "[{workflow_name}]({run_url}) started processing this {event_type}." + ] }, "run-success": { "type": "string", "description": "Custom message template for successful workflow completion. Available placeholders: {workflow_name}, {run_url}. Default: '\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.'", - "examples": ["\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.", "\u2705 [{workflow_name}]({run_url}) finished."] + "examples": [ + "\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.", + "\u2705 [{workflow_name}]({run_url}) finished." + ] }, "run-failure": { "type": "string", "description": "Custom message template for failed workflow. Available placeholders: {workflow_name}, {run_url}, {status}. Default: '\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'", - "examples": ["\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", "\u274c [{workflow_name}]({run_url}) {status}."] + "examples": [ + "\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", + "\u274c [{workflow_name}]({run_url}) {status}." + ] }, "detection-failure": { "type": "string", "description": "Custom message template for detection job failure. Available placeholders: {workflow_name}, {run_url}. Default: '\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.'", - "examples": ["\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", "\u26a0\ufe0f Detection job failed in [{workflow_name}]({run_url})."] + "examples": [ + "\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", + "\u26a0\ufe0f Detection job failed in [{workflow_name}]({run_url})." + ] }, "agent-failure-issue": { "type": "string", "description": "Custom footer template for agent failure tracking issues. Available placeholders: {workflow_name}, {run_url}. Default: '> Agent failure tracked by [{workflow_name}]({run_url})'", - "examples": ["> Agent failure tracked by [{workflow_name}]({run_url})", "> Failure report from [{workflow_name}]({run_url})"] - }, + "examples": [ + "> Agent failure tracked by [{workflow_name}]({run_url})", + "> Failure report from [{workflow_name}]({run_url})" + ] + }, "agent-failure-comment": { "type": "string", "description": "Custom footer template for comments on agent failure tracking issues. Available placeholders: {workflow_name}, {run_url}. Default: '> Agent failure update from [{workflow_name}]({run_url})'", - "examples": ["> Agent failure update from [{workflow_name}]({run_url})", "> Update from [{workflow_name}]({run_url})"] + "examples": [ + "> Agent failure update from [{workflow_name}]({run_url})", + "> Update from [{workflow_name}]({run_url})" + ] }, "pull-request-created": { "type": "string", "description": "Custom message template for pull request creation link appended to the activation comment. Available placeholders: {item_number}, {item_url}. Default: 'Pull request created: [#{item_number}]({item_url})'", - "examples": ["Pull request created: [#{item_number}]({item_url})", "[#{item_number}]({item_url}) opened"] + "examples": [ + "Pull request created: [#{item_number}]({item_url})", + "[#{item_number}]({item_url}) opened" + ] }, "issue-created": { "type": "string", "description": "Custom message template for issue creation link appended to the activation comment. Available placeholders: {item_number}, {item_url}. Default: 'Issue created: [#{item_number}]({item_url})'", - "examples": ["Issue created: [#{item_number}]({item_url})", "[#{item_number}]({item_url}) filed"] + "examples": [ + "Issue created: [#{item_number}]({item_url})", + "[#{item_number}]({item_url}) filed" + ] }, "commit-pushed": { "type": "string", "description": "Custom message template for commit push link appended to the activation comment. Available placeholders: {commit_sha}, {short_sha}, {commit_url}. Default: 'Commit pushed: [`{short_sha}`]({commit_url})'", - "examples": ["Commit pushed: [`{short_sha}`]({commit_url})", "[`{short_sha}`]({commit_url}) pushed"] + "examples": [ + "Commit pushed: [`{short_sha}`]({commit_url})", + "[`{short_sha}`]({commit_url}) pushed" + ] }, "body-header": { "type": "string", "description": "Custom header text prepended to every message body generated by safe outputs (issues, comments, pull requests, discussions). Applied after any threat-detection caution alert and before the agent-generated content. Available placeholders: {workflow_name}, {run_url}.", - "examples": ["> \u26a0\ufe0f This content was generated by [{workflow_name}]({run_url}).", "> \ud83e\udd16 AI-generated output \u2014 please review before acting."] + "examples": [ + "> \u26a0\ufe0f This content was generated by [{workflow_name}]({run_url}).", + "> \ud83e\udd16 AI-generated output \u2014 please review before acting." + ] }, "append-only-comments": { "type": "boolean", @@ -9306,31 +10499,50 @@ "type": "boolean", "description": "Global footer control for all safe outputs. When false, omits visible AI-generated footer content from all created/updated entities (issues, PRs, discussions, releases) while still including XML markers for searchability. Individual safe-output types (create-issue, update-issue, etc.) can override this by specifying their own footer field. Defaults to true.", "default": true, - "examples": [false, true] + "examples": [ + false, + true + ] }, "activation-comments": { - "type": ["boolean", "string"], + "type": [ + "boolean", + "string" + ], "description": "When set to false or \"false\", disables all activation and fallback comments entirely (run-started, run-success, run-failure, PR/issue creation links). Supports templatable boolean values including GitHub Actions expressions (e.g. ${{ inputs.activation-comments }}). Default: true", "default": true, - "examples": [false, true, "${{ inputs.activation-comments }}"] + "examples": [ + false, + true, + "${{ inputs.activation-comments }}" + ] }, "group-reports": { "type": "boolean", "description": "When true, creates a parent '[aw] Failed runs' issue that tracks all workflow failures as sub-issues. Helps organize failure tracking but may be unnecessary in smaller repositories. Defaults to false.", "default": false, - "examples": [false, true] + "examples": [ + false, + true + ] }, "report-failure-as-issue": { "type": "boolean", "description": "When false, disables creating failure tracking issues when workflows fail. Useful for workflows where failures are expected or handled elsewhere. Defaults to true.", "default": true, - "examples": [false, true] + "examples": [ + false, + true + ] }, "failure-issue-repo": { "type": "string", "description": "Repository to create failure tracking issues in, in the format 'owner/repo'. Useful when the current repository has issues disabled. Defaults to the current repository.", "pattern": "^[^/]+/[^/]+$", - "examples": ["github/docs-engineering", "myorg/infra-alerts"] + "examples": [ + "github/docs-engineering", + "myorg/infra-alerts" + ] }, "max-bot-mentions": { "description": "Maximum number of bot trigger references (e.g. 'fixes #123', 'closes #456') allowed in output before all of them are neutralized. Default: 10. Supports integer or GitHub Actions expression (e.g. '${{ inputs.max-bot-mentions }}').", @@ -9349,14 +10561,23 @@ }, "id-token": { "type": "string", - "enum": ["write", "none"], + "enum": [ + "write", + "none" + ], "description": "Override the id-token permission for the safe-outputs job. Use 'write' to force-enable the id-token: write permission (required for OIDC authentication with cloud providers). Use 'none' to suppress automatic detection and prevent adding id-token: write even when vault/OIDC actions are detected in steps. By default, the compiler auto-detects known OIDC/vault actions (aws-actions/configure-aws-credentials, azure/login, google-github-actions/auth, hashicorp/vault-action, cyberark/conjur-action) and adds id-token: write automatically.", - "examples": ["write", "none"] + "examples": [ + "write", + "none" + ] }, "concurrency-group": { "type": "string", "description": "Concurrency group for the safe-outputs job. When set, the safe-outputs job will use this concurrency group with cancel-in-progress: false. Supports GitHub Actions expressions.", - "examples": ["my-workflow-safe-outputs", "safe-outputs-${{ github.repository }}"] + "examples": [ + "my-workflow-safe-outputs", + "safe-outputs-${{ github.repository }}" + ] }, "needs": { "type": "array", @@ -9368,7 +10589,11 @@ "additionalItems": false, "uniqueItems": true, "default": [], - "examples": [["secrets_fetcher"]] + "examples": [ + [ + "secrets_fetcher" + ] + ] }, "environment": { "description": "Override the GitHub deployment environment for the safe-outputs job. When set, this environment is used instead of the top-level environment: field. When not set, the top-level environment: field is propagated automatically so that environment-scoped secrets are accessible in the safe-outputs job.", @@ -9390,7 +10615,9 @@ "description": "A deployment URL" } }, - "required": ["name"], + "required": [ + "name" + ], "additionalProperties": false } ] @@ -9425,7 +10652,11 @@ "uses": { "type": "string", "description": "The GitHub Action to use. Supports owner/repo@ref, owner/repo/subdir@ref, or ./local/path.", - "examples": ["actions-ecosystem/action-add-labels@v1", "owner/repo@v1", "owner/repo/subdir@v1"] + "examples": [ + "actions-ecosystem/action-add-labels@v1", + "owner/repo@v1", + "owner/repo/subdir@v1" + ] }, "description": { "type": "string", @@ -9470,7 +10701,9 @@ "additionalProperties": false } }, - "required": ["uses"], + "required": [ + "uses" + ], "additionalProperties": false } } @@ -9520,7 +10753,10 @@ "staged": { "type": "boolean", "description": "If true, emit step summary messages instead of making GitHub API calls for this specific output type (preview mode)", - "examples": [true, false] + "examples": [ + true, + false + ] } }, "additionalProperties": false @@ -9580,7 +10816,9 @@ { "type": "object", "description": "A single OTLP endpoint with a URL and optional per-endpoint headers.", - "required": ["url"], + "required": [ + "url" + ], "properties": { "url": { "type": "string", @@ -9610,7 +10848,9 @@ "items": { "type": "object", "description": "A single OTLP endpoint with a URL and optional per-endpoint headers.", - "required": ["url"], + "required": [ + "url" + ], "properties": { "url": { "type": "string", @@ -9655,7 +10895,11 @@ }, "if-missing": { "type": "string", - "enum": ["error", "warn", "ignore"], + "enum": [ + "error", + "warn", + "ignore" + ], "default": "error", "description": "How to handle missing OTLP endpoint/header values at runtime (for example from unset secrets). 'error' fails workflow startup (default), 'warn' logs a warning and skips MCP gateway OTLP configuration, and 'ignore' skips MCP gateway OTLP configuration without warning. This affects MCP gateway setup only; workflow-level OTEL_* environment variables are still injected." } @@ -9668,7 +10912,9 @@ "user-rate-limit": { "type": "object", "description": "Rate limiting configuration to restrict how frequently users can trigger the workflow. Helps prevent abuse and resource exhaustion from programmatically triggered events.", - "required": ["max-runs-per-window"], + "required": [ + "max-runs-per-window" + ], "properties": { "max-runs-per-window": { "description": "Maximum number of workflow runs allowed per user within the time window. Required field. Supports integer or GitHub Actions expression (e.g. '${{ inputs.max }}').", @@ -9697,7 +10943,16 @@ "description": "Optional list of event types to apply rate limiting to. If not specified, rate limiting applies to all programmatically triggered events (e.g., workflow_dispatch, issue_comment, pull_request_review).", "items": { "type": "string", - "enum": ["workflow_dispatch", "issue_comment", "pull_request_review", "pull_request_review_comment", "issues", "pull_request", "discussion_comment", "discussion"] + "enum": [ + "workflow_dispatch", + "issue_comment", + "pull_request_review", + "pull_request_review_comment", + "issues", + "pull_request", + "discussion_comment", + "discussion" + ] }, "minItems": 1 }, @@ -9706,7 +10961,13 @@ "description": "Optional list of roles that are exempt from rate limiting. Defaults to ['admin', 'maintain', 'write'] if not specified. Users with any of these roles will not be subject to rate limiting checks. To apply rate limiting to all users, set to an empty array: []", "items": { "type": "string", - "enum": ["admin", "maintain", "write", "triage", "read"] + "enum": [ + "admin", + "maintain", + "write", + "triage", + "read" + ] }, "minItems": 0 } @@ -9720,12 +10981,18 @@ { "max-runs-per-window": 10, "window": 30, - "events": ["workflow_dispatch", "issue_comment"] + "events": [ + "workflow_dispatch", + "issue_comment" + ] }, { "max-runs-per-window": 5, "window": 60, - "ignored-roles": ["admin", "maintain"] + "ignored-roles": [ + "admin", + "maintain" + ] } ] }, @@ -9775,7 +11042,16 @@ "description": "Optional list of event types to apply rate limiting to.", "items": { "type": "string", - "enum": ["workflow_dispatch", "issue_comment", "pull_request_review", "pull_request_review_comment", "issues", "pull_request", "discussion_comment", "discussion"] + "enum": [ + "workflow_dispatch", + "issue_comment", + "pull_request_review", + "pull_request_review_comment", + "issues", + "pull_request", + "discussion_comment", + "discussion" + ] }, "minItems": 1 }, @@ -9784,7 +11060,13 @@ "description": "Optional list of roles that are exempt from rate limiting.", "items": { "type": "string", - "enum": ["admin", "maintain", "write", "triage", "read"] + "enum": [ + "admin", + "maintain", + "write", + "triage", + "read" + ] }, "minItems": 0 } @@ -9796,25 +11078,37 @@ "default": true, "$comment": "Strict mode enforces several security constraints that are validated in Go code (pkg/workflow/strict_mode_validation.go) rather than JSON Schema: (1) Write Permissions + Safe Outputs: When strict=true AND permissions contains write values (contents:write, issues:write, pull-requests:write), safe-outputs must be configured. This relationship is too complex for JSON Schema as it requires checking if ANY permission property has a 'write' value. (2) Network Requirements: When strict=true, the 'network' field must be present and cannot contain standalone wildcard '*' (but patterns like '*.example.com' ARE allowed). (3) MCP Container Network: Custom MCP servers with containers require explicit network configuration. (4) Action Pinning: Actions must be pinned to commit SHAs. These are enforced during compilation via validateStrictMode().", "description": "Enable strict mode validation for enhanced security and compliance. Strict mode enforces: (1) Write Permissions - refuses contents:write, issues:write, pull-requests:write; requires safe-outputs instead, (2) Network Configuration - requires explicit network configuration with no standalone wildcard '*' in allowed domains (patterns like '*.example.com' are allowed), (3) Action Pinning - enforces actions pinned to commit SHAs instead of tags/branches, (4) MCP Network - requires network configuration for custom MCP servers with containers, (5) Deprecated Fields - refuses deprecated frontmatter fields. Can be enabled per-workflow via 'strict: true' in frontmatter, or disabled via 'strict: false'. CLI flag takes precedence over frontmatter (gh aw compile --strict enforces strict mode). Defaults to true. See: https://github.github.com/gh-aw/reference/frontmatter/#strict-mode-strict", - "examples": [true, false] + "examples": [ + true, + false + ] }, "private": { "type": "boolean", "default": false, "description": "Mark the workflow as private, preventing it from being added to other repositories via 'gh aw add'. A workflow with private: true is not meant to be shared outside its repository.", - "examples": [true, false] + "examples": [ + true, + false + ] }, "check-for-updates": { "type": "boolean", "default": true, "description": "Control whether the compile-agentic version update check runs in the activation job. When true (default), the activation job downloads config.json from the gh-aw repository and verifies the compiled version is not blocked and meets the minimum supported version. Set to false to disable the check (not allowed in strict mode). See: https://github.github.com/gh-aw/reference/frontmatter/#check-for-updates", - "examples": [true, false] + "examples": [ + true, + false + ] }, "run-install-scripts": { "type": "boolean", "default": false, "description": "Allow npm pre/post install scripts to execute during package installation. By default, --ignore-scripts is added to all generated npm install commands to prevent supply chain attacks via malicious install hooks. Setting run-install-scripts: true disables this protection globally (all runtimes). A supply chain security warning is emitted at compile time; in strict mode this is an error. Per-runtime control is also available via runtimes..run-install-scripts. See: https://github.github.com/gh-aw/reference/frontmatter/#run-install-scripts", - "examples": [false, true] + "examples": [ + false, + true + ] }, "mcp-scripts": { "type": "object", @@ -9823,7 +11117,9 @@ "^([a-ln-z][a-z0-9_-]*|m[a-np-z][a-z0-9_-]*|mo[a-ce-z][a-z0-9_-]*|mod[a-df-z][a-z0-9_-]*|mode[a-z0-9_-]+)$": { "type": "object", "description": "Custom tool definition. The key is the tool name (lowercase alphanumeric with dashes/underscores).", - "required": ["description"], + "required": [ + "description" + ], "properties": { "description": { "type": "string", @@ -9837,7 +11133,13 @@ "properties": { "type": { "type": "string", - "enum": ["string", "number", "boolean", "array", "object"], + "enum": [ + "string", + "number", + "boolean", + "array", + "object" + ], "default": "string", "description": "The JSON schema type of the input parameter." }, @@ -9891,71 +11193,108 @@ "description": "Timeout in seconds for tool execution. Default is 60 seconds. Applies to shell (run) and Python (py) tools.", "default": 60, "minimum": 1, - "examples": [30, 60, 120, 300] + "examples": [ + 30, + 60, + 120, + 300 + ] } }, "additionalProperties": false, "oneOf": [ { - "required": ["script"], + "required": [ + "script" + ], "not": { "anyOf": [ { - "required": ["run"] + "required": [ + "run" + ] }, { - "required": ["py"] + "required": [ + "py" + ] }, { - "required": ["go"] + "required": [ + "go" + ] } ] } }, { - "required": ["run"], + "required": [ + "run" + ], "not": { "anyOf": [ { - "required": ["script"] + "required": [ + "script" + ] }, { - "required": ["py"] + "required": [ + "py" + ] }, { - "required": ["go"] + "required": [ + "go" + ] } ] } }, { - "required": ["py"], + "required": [ + "py" + ], "not": { "anyOf": [ { - "required": ["script"] + "required": [ + "script" + ] }, { - "required": ["run"] + "required": [ + "run" + ] }, { - "required": ["go"] + "required": [ + "go" + ] } ] } }, { - "required": ["go"], + "required": [ + "go" + ], "not": { "anyOf": [ { - "required": ["script"] + "required": [ + "script" + ] }, { - "required": ["run"] + "required": [ + "run" + ] }, { - "required": ["py"] + "required": [ + "py" + ] } ] } @@ -10013,9 +11352,18 @@ "description": "Runtime configuration object identified by runtime ID (e.g., 'node', 'python', 'go')", "properties": { "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Runtime version as a string (e.g., '22', '3.12', 'latest') or number (e.g., 22, 3.12). Numeric values are automatically converted to strings at runtime.", - "examples": ["22", "3.12", "latest", 22, 3.12] + "examples": [ + "22", + "3.12", + "latest", + 22, + 3.12 + ] }, "action-repo": { "type": "string", @@ -10028,13 +11376,22 @@ "if": { "type": "string", "description": "Optional GitHub Actions if condition to control when the runtime setup step runs. Supports standard GitHub Actions expression syntax. Useful for conditionally installing runtimes based on file presence (e.g., \"hashFiles('go.mod') != ''\" to install Go only when go.mod exists).", - "examples": ["hashFiles('go.mod') != ''", "hashFiles('package.json') != ''", "hashFiles('requirements.txt') != '' || hashFiles('pyproject.toml') != ''", "hashFiles('uv.lock') != ''", "github.event_name == 'workflow_dispatch'"] + "examples": [ + "hashFiles('go.mod') != ''", + "hashFiles('package.json') != ''", + "hashFiles('requirements.txt') != '' || hashFiles('pyproject.toml') != ''", + "hashFiles('uv.lock') != ''", + "github.event_name == 'workflow_dispatch'" + ] }, "run-install-scripts": { "type": "boolean", "default": false, "description": "Allow npm pre/post install scripts to execute for this runtime during package installation. Overrides the global run-install-scripts setting for this specific runtime. Only affects runtimes that generate npm install commands (node). A supply chain security warning is emitted at compile time; in strict mode this is an error.", - "examples": [false, true] + "examples": [ + false, + true + ] } }, "additionalProperties": false @@ -10058,7 +11415,9 @@ }, { "type": "boolean", - "enum": [false], + "enum": [ + false + ], "description": "Set to false to disable the default checkout step. The agent job will not check out any repository (dev-mode checkouts are unaffected)." } ] @@ -10090,7 +11449,13 @@ }, "type": { "type": "string", - "enum": ["string", "number", "boolean", "choice", "array"], + "enum": [ + "string", + "number", + "boolean", + "choice", + "array" + ], "description": "The type of the input value." }, "options": { @@ -10106,7 +11471,11 @@ "properties": { "type": { "type": "string", - "enum": ["string", "number", "boolean"], + "enum": [ + "string", + "number", + "boolean" + ], "description": "Type of each array item." } }, @@ -10118,7 +11487,9 @@ { "type": "object", "description": "Input parameter definition for object type (one level deep). Use 'properties' to declare the expected sub-fields.", - "required": ["type"], + "required": [ + "type" + ], "properties": { "description": { "type": "string", @@ -10131,7 +11502,9 @@ }, "type": { "type": "string", - "enum": ["object"], + "enum": [ + "object" + ], "description": "The type 'object' enables structured sub-fields accessible via 'github.aw.import-inputs..'." }, "properties": { @@ -10151,7 +11524,12 @@ "default": {}, "type": { "type": "string", - "enum": ["string", "number", "boolean", "choice"], + "enum": [ + "string", + "number", + "boolean", + "choice" + ], "description": "Type of the sub-property." }, "options": { @@ -10230,13 +11608,17 @@ "const": "centralized" } }, - "required": ["strategy"] + "required": [ + "strategy" + ] } } ] } }, - "required": ["slash_command"] + "required": [ + "slash_command" + ] }, { "properties": { @@ -10246,7 +11628,9 @@ } } }, - "required": ["command"] + "required": [ + "command" + ] } ] } @@ -10265,7 +11649,9 @@ } } }, - "required": ["issue_comment"] + "required": [ + "issue_comment" + ] }, { "properties": { @@ -10275,7 +11661,9 @@ } } }, - "required": ["pull_request_review_comment"] + "required": [ + "pull_request_review_comment" + ] }, { "properties": { @@ -10285,7 +11673,9 @@ } } }, - "required": ["label"] + "required": [ + "label" + ] } ] } @@ -10458,9 +11848,18 @@ "description": "AI engine identifier: built-in ('claude', 'codex', 'copilot', 'gemini', 'opencode', 'crush', 'pi') or a named catalog entry" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version of the AI engine action (e.g., 'beta', 'stable', 20). Has sensible defaults and can typically be omitted. Numeric values are automatically converted to strings at runtime. GitHub Actions expressions (e.g., '${{ inputs.engine-version }}') are accepted and compiled with injection-safe env var handling.", - "examples": ["beta", "stable", 20, 3.11, "${{ inputs.engine-version }}"] + "examples": [ + "beta", + "stable", + 20, + 3.11, + "${{ inputs.engine-version }}" + ] }, "model": { "type": "string", @@ -10504,11 +11903,16 @@ }, "queue": { "type": "string", - "enum": ["single", "max"], + "enum": [ + "single", + "max" + ], "description": "Pending run queue behavior for this concurrency group. 'single' (default) allows one pending run and replaces older pending runs. 'max' allows up to 100 pending runs in FIFO order." } }, - "required": ["group"], + "required": [ + "group" + ], "additionalProperties": false } ], @@ -10540,7 +11944,9 @@ "properties": { "type": { "type": "string", - "enum": ["github-oidc"], + "enum": [ + "github-oidc" + ], "description": "Authentication type. Currently only 'github-oidc' is supported." }, "audience": { @@ -10565,7 +11971,9 @@ "description": "Optional Azure cloud name (for example, public, usgovernment, china)." } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": false }, "config": { @@ -10579,7 +11987,11 @@ "api-target": { "type": "string", "description": "Custom API endpoint hostname for the agentic engine. Used for GitHub Enterprise Cloud (GHEC), GitHub Enterprise Server (GHES), or custom AI endpoints. Example: 'api.acme.ghe.com' for GHEC, 'api.enterprise.githubcopilot.com' for GHES, or custom endpoint hostnames.", - "examples": ["api.acme.ghe.com", "api.enterprise.githubcopilot.com", "api.custom.endpoint.com"] + "examples": [ + "api.acme.ghe.com", + "api.enterprise.githubcopilot.com", + "api.custom.endpoint.com" + ] }, "token-weights": { "type": "object", @@ -10646,6 +12058,16 @@ "description": "When true, disables automatic loading of context and custom instructions by the AI engine. The engine-specific flag depends on the engine: copilot uses --no-custom-instructions (suppresses .github/AGENTS.md and user-level custom instructions), claude uses --bare (suppresses CLAUDE.md memory files), codex uses --no-system-prompt (suppresses the default system prompt), gemini sets GEMINI_SYSTEM_MD=/dev/null (overrides the built-in system prompt with an empty one). Defaults to false.", "default": false }, + "permission-mode": { + "type": "string", + "description": "Claude-only: explicit permission mode for Claude Code CLI. Controls how Claude handles tool and file access permissions during headless execution. Valid values: auto, acceptEdits, plan, bypassPermissions. When not set, the compiler auto-selects based on tool configuration (bypassPermissions when bash wildcard is present, acceptEdits otherwise). Set to auto to let Claude decide interactively.", + "enum": [ + "auto", + "acceptEdits", + "plan", + "bypassPermissions" + ] + }, "mcp": { "type": "object", "description": "Engine-level MCP gateway configuration. Settings here apply to the MCP gateway used by this engine.", @@ -10653,18 +12075,31 @@ "session-timeout": { "type": "string", "description": "Session timeout for MCP gateway sessions as a Go duration string (e.g. \"30m\", \"4h\", \"24h\"). Must be at least 5m (no upper bound). Omitted or empty uses the effective gateway default (precedence: this field > MCP_GATEWAY_SESSION_TIMEOUT env var > built-in default 6h). Longer timeouts benefit multi-hour workflows such as large-scale migrations; shorter values free gateway resources sooner.", - "examples": ["30m", "1h", "4h", "6h", "12h"] + "examples": [ + "30m", + "1h", + "4h", + "6h", + "12h" + ] }, "tool-timeout": { "type": "string", "description": "Timeout for individual MCP tool calls as a Go duration string (e.g. \"30s\", \"2m\", \"10m\"). Must be between 10s and 600s inclusive. Omitted or empty uses the gateway built-in default (60s). Use a higher value for slow MCP backends such as full-text search over large indexes.", - "examples": ["30s", "2m", "5m", "10m"] + "examples": [ + "30s", + "2m", + "5m", + "10m" + ] } }, "additionalProperties": false } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, { @@ -10678,15 +12113,32 @@ "id": { "type": "string", "description": "Runtime adapter identifier (e.g. 'codex', 'claude', 'copilot', 'gemini', 'opencode', 'crush', 'pi')", - "examples": ["codex", "claude", "copilot", "gemini", "opencode", "crush", "pi"] + "examples": [ + "codex", + "claude", + "copilot", + "gemini", + "opencode", + "crush", + "pi" + ] }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version of the runtime adapter (e.g. '0.105.0', 'beta')", - "examples": ["0.105.0", "beta", "latest"] + "examples": [ + "0.105.0", + "beta", + "latest" + ] } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "provider": { @@ -10696,12 +12148,21 @@ "id": { "type": "string", "description": "Provider identifier (e.g. 'openai', 'anthropic', 'github', 'google')", - "examples": ["openai", "anthropic", "github", "google"] + "examples": [ + "openai", + "anthropic", + "github", + "google" + ] }, "model": { "type": "string", "description": "Optional specific LLM model to use (e.g. 'gpt-5', 'claude-3-5-sonnet-20241022')", - "examples": ["gpt-5", "claude-3-5-sonnet-20241022", "gpt-4o"] + "examples": [ + "gpt-5", + "claude-3-5-sonnet-20241022", + "gpt-4o" + ] }, "auth": { "type": "object", @@ -10710,37 +12171,58 @@ "secret": { "type": "string", "description": "Name of the GitHub Actions secret that contains the API key for this provider", - "examples": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "CUSTOM_API_KEY"] + "examples": [ + "OPENAI_API_KEY", + "ANTHROPIC_API_KEY", + "CUSTOM_API_KEY" + ] }, "strategy": { "type": "string", - "enum": ["api-key", "oauth-client-credentials", "bearer"], + "enum": [ + "api-key", + "oauth-client-credentials", + "bearer" + ], "description": "Authentication strategy for the provider (default: api-key when secret is set)" }, "token-url": { "type": "string", "description": "OAuth 2.0 token endpoint URL. Required when strategy is 'oauth-client-credentials'.", - "examples": ["https://auth.example.com/oauth/token"] + "examples": [ + "https://auth.example.com/oauth/token" + ] }, "client-id": { "type": "string", "description": "GitHub Actions secret name that holds the OAuth client ID. Required when strategy is 'oauth-client-credentials'.", - "examples": ["OAUTH_CLIENT_ID"] + "examples": [ + "OAUTH_CLIENT_ID" + ] }, "client-secret": { "type": "string", "description": "GitHub Actions secret name that holds the OAuth client secret. Required when strategy is 'oauth-client-credentials'.", - "examples": ["OAUTH_CLIENT_SECRET"] + "examples": [ + "OAUTH_CLIENT_SECRET" + ] }, "token-field": { "type": "string", "description": "JSON field name in the token response that contains the access token. Defaults to 'access_token'.", - "examples": ["access_token", "token"] + "examples": [ + "access_token", + "token" + ] }, "header-name": { "type": "string", "description": "HTTP header name to inject the API key or token into (e.g. 'api-key', 'x-api-key'). Required when strategy is not 'bearer'.", - "examples": ["api-key", "x-api-key", "Authorization"] + "examples": [ + "api-key", + "x-api-key", + "Authorization" + ] } }, "additionalProperties": false @@ -10752,7 +12234,9 @@ "path-template": { "type": "string", "description": "URL path template with {model} and other variable placeholders (e.g. '/openai/deployments/{model}/chat/completions')", - "examples": ["/openai/deployments/{model}/chat/completions"] + "examples": [ + "/openai/deployments/{model}/chat/completions" + ] }, "query": { "type": "object", @@ -10788,9 +12272,21 @@ "type": "boolean", "description": "When true, disables automatic loading of context and custom instructions by the AI engine. The engine-specific flag depends on the engine: copilot uses --no-custom-instructions, claude uses --bare, codex uses --no-system-prompt, gemini sets GEMINI_SYSTEM_MD=/dev/null. Defaults to false.", "default": false + }, + "permission-mode": { + "type": "string", + "description": "Claude-only: explicit permission mode for Claude Code CLI. Controls how Claude handles tool and file access permissions during headless execution. Valid values: auto, acceptEdits, plan, bypassPermissions. When not set, the compiler auto-selects based on tool configuration (bypassPermissions when bash wildcard is present, acceptEdits otherwise). Set to auto to let Claude decide interactively.", + "enum": [ + "auto", + "acceptEdits", + "plan", + "bypassPermissions" + ] } }, - "required": ["runtime"], + "required": [ + "runtime" + ], "additionalProperties": false }, { @@ -10831,7 +12327,11 @@ }, "strategy": { "type": "string", - "enum": ["api-key", "oauth-client-credentials", "bearer"], + "enum": [ + "api-key", + "oauth-client-credentials", + "bearer" + ], "description": "Authentication strategy" }, "token-url": { @@ -10918,7 +12418,10 @@ "description": "Name of the GitHub Actions secret that provides credentials for this role" } }, - "required": ["role", "secret"], + "required": [ + "role", + "secret" + ], "additionalProperties": false } }, @@ -10928,7 +12431,10 @@ "additionalProperties": true } }, - "required": ["id", "display-name"], + "required": [ + "id", + "display-name" + ], "additionalProperties": false }, { @@ -10942,18 +12448,31 @@ "session-timeout": { "type": "string", "description": "Session timeout for MCP gateway sessions as a Go duration string (e.g. \"30m\", \"4h\", \"24h\"). Must be at least 5m (no upper bound). Omitted or empty uses the effective gateway default (precedence: this field > MCP_GATEWAY_SESSION_TIMEOUT env var > built-in default 6h).", - "examples": ["30m", "1h", "4h", "6h", "12h"] + "examples": [ + "30m", + "1h", + "4h", + "6h", + "12h" + ] }, "tool-timeout": { "type": "string", "description": "Timeout for individual MCP tool calls as a Go duration string (e.g. \"30s\", \"2m\", \"10m\"). Must be between 10s and 600s inclusive. Omitted or empty uses the gateway built-in default (60s). Use a higher value for slow MCP backends such as full-text search over large indexes.", - "examples": ["30s", "2m", "5m", "10m"] + "examples": [ + "30s", + "2m", + "5m", + "10m" + ] } }, "additionalProperties": false } }, - "required": ["mcp"], + "required": [ + "mcp" + ], "additionalProperties": false }, { @@ -10965,7 +12484,9 @@ "description": "Model preference or size category (e.g. 'small', 'large', 'gpt-4.1'). Applied to the default engine when engine.id is not specified." } }, - "required": ["model"], + "required": [ + "model" + ], "additionalProperties": false } ] @@ -10976,13 +12497,18 @@ "properties": { "type": { "type": "string", - "enum": ["stdio", "local"], + "enum": [ + "stdio", + "local" + ], "description": "MCP connection type for stdio (local is an alias for stdio)" }, "registry": { "type": "string", "description": "URI to the installation location when MCP is installed from a registry", - "examples": ["https://api.mcp.github.com/v0/servers/microsoft/markitdown"] + "examples": [ + "https://api.mcp.github.com/v0/servers/microsoft/markitdown" + ] }, "command": { "type": "string", @@ -10997,9 +12523,17 @@ "description": "Container image for stdio MCP connections" }, "version": { - "type": ["string", "number"], + "type": [ + "string", + "number" + ], "description": "Optional version/tag for the container image (e.g., 'latest', 'v1.0.0', 20, 3.11). Numeric values are automatically converted to strings at runtime.", - "examples": ["latest", "v1.0.0", 20, 3.11] + "examples": [ + "latest", + "v1.0.0", + 20, + 3.11 + ] }, "args": { "type": "array", @@ -11011,7 +12545,11 @@ "entrypoint": { "type": "string", "description": "Optional entrypoint override for container (equivalent to docker run --entrypoint)", - "examples": ["/bin/sh", "/custom/entrypoint.sh", "python"] + "examples": [ + "/bin/sh", + "/custom/entrypoint.sh", + "python" + ] }, "entrypointArgs": { "type": "array", @@ -11027,7 +12565,15 @@ "pattern": "^[^:]+:[^:]+:(ro|rw)$" }, "description": "Volume mounts for container in format 'source:dest:mode' where mode is 'ro' or 'rw'", - "examples": [["/tmp/data:/data:ro"], ["/workspace:/workspace:rw", "/config:/config:ro"]] + "examples": [ + [ + "/tmp/data:/data:ro" + ], + [ + "/workspace:/workspace:rw", + "/config:/config:ro" + ] + ] }, "env": { "type": "object", @@ -11074,7 +12620,18 @@ "items": { "type": "string" }, - "examples": [["*"], ["store_memory", "retrieve_memory"], ["brave_web_search"]] + "examples": [ + [ + "*" + ], + [ + "store_memory", + "retrieve_memory" + ], + [ + "brave_web_search" + ] + ] }, "proxy-args": { "type": "array", @@ -11088,22 +12645,32 @@ "$comment": "Validation constraints: (1) Mutual exclusion: 'command' and 'container' cannot both be specified. (2) Requirement: Either 'command' or 'container' must be provided (via 'anyOf'). (3) Type constraint: When 'type' is 'stdio' or 'local', either 'command' or 'container' is required. Note: Per-server 'network' field is deprecated and ignored.", "anyOf": [ { - "required": ["type"] + "required": [ + "type" + ] }, { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ], "not": { "allOf": [ { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ] }, @@ -11112,17 +12679,24 @@ "if": { "properties": { "type": { - "enum": ["stdio", "local"] + "enum": [ + "stdio", + "local" + ] } } }, "then": { "anyOf": [ { - "required": ["command"] + "required": [ + "command" + ] }, { - "required": ["container"] + "required": [ + "container" + ] } ] } @@ -11135,13 +12709,17 @@ "properties": { "type": { "type": "string", - "enum": ["http"], + "enum": [ + "http" + ], "description": "MCP connection type for HTTP" }, "registry": { "type": "string", "description": "URI to the installation location when MCP is installed from a registry", - "examples": ["https://api.mcp.github.com/v0/servers/microsoft/markitdown"] + "examples": [ + "https://api.mcp.github.com/v0/servers/microsoft/markitdown" + ] }, "url": { "type": "string", @@ -11164,13 +12742,26 @@ "items": { "type": "string" }, - "examples": [["*"], ["store_memory", "retrieve_memory"], ["brave_web_search"]] + "examples": [ + [ + "*" + ], + [ + "store_memory", + "retrieve_memory" + ], + [ + "brave_web_search" + ] + ] }, "auth": { "$ref": "#/$defs/http_mcp_auth" } }, - "required": ["url"], + "required": [ + "url" + ], "additionalProperties": false }, "http_mcp_auth": { @@ -11179,7 +12770,9 @@ "properties": { "type": { "type": "string", - "enum": ["github-oidc"], + "enum": [ + "github-oidc" + ], "description": "Authentication type. Currently only 'github-oidc' is supported, which acquires short-lived JWTs from the GitHub Actions OIDC endpoint." }, "audience": { @@ -11188,14 +12781,21 @@ "format": "uri" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": false }, "github_token": { "type": "string", "pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$", "description": "GitHub token expression. Accepts a secrets expression (e.g., `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`) or a job output expression (e.g., `${{ needs.auth.outputs.token }}`). Pattern details: secret names match `[A-Za-z_][A-Za-z0-9_]*`; job IDs and output names in dot notation match `[A-Za-z_][A-Za-z0-9_]*` (identifiers without hyphens).", - "examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", "${{ needs.auth.outputs.token }}"] + "examples": [ + "${{ secrets.GITHUB_TOKEN }}", + "${{ secrets.CUSTOM_PAT }}", + "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "${{ needs.auth.outputs.token }}" + ] }, "github_app": { "type": "object", @@ -11204,17 +12804,23 @@ "app-id": { "type": "string", "description": "Deprecated alias for client-id. GitHub App ID/client ID (e.g., '${{ vars.APP_ID }}').", - "examples": ["${{ vars.APP_ID }}"] + "examples": [ + "${{ vars.APP_ID }}" + ] }, "client-id": { "type": "string", "description": "GitHub App client ID (e.g., '${{ vars.APP_ID }}'). Required to mint a GitHub App token.", - "examples": ["${{ vars.APP_ID }}"] + "examples": [ + "${{ vars.APP_ID }}" + ] }, "private-key": { "type": "string", "description": "GitHub App private key (e.g., '${{ secrets.APP_PRIVATE_KEY }}'). Required to mint a GitHub App token.", - "examples": ["${{ secrets.APP_PRIVATE_KEY }}"] + "examples": [ + "${{ secrets.APP_PRIVATE_KEY }}" + ] }, "ignore-if-missing": { "type": "boolean", @@ -11238,10 +12844,16 @@ }, "anyOf": [ { - "required": ["client-id", "private-key"] + "required": [ + "client-id", + "private-key" + ] }, { - "required": ["app-id", "private-key"] + "required": [ + "app-id", + "private-key" + ] } ], "additionalProperties": false, @@ -11356,10 +12968,14 @@ "additionalProperties": false, "anyOf": [ { - "required": ["uses"] + "required": [ + "uses" + ] }, { - "required": ["run"] + "required": [ + "run" + ] } ] }, @@ -11371,34 +12987,56 @@ "repository": { "type": "string", "description": "Repository to checkout in owner/repo format. Defaults to the current repository.", - "examples": ["owner/repo", "github/gh-aw"] + "examples": [ + "owner/repo", + "github/gh-aw" + ] }, "ref": { "type": "string", "description": "Branch, tag, or SHA to checkout. Defaults to the ref that triggered the workflow.", - "examples": ["main", "v1.0.0", "feature/my-branch"] + "examples": [ + "main", + "v1.0.0", + "feature/my-branch" + ] }, "path": { "type": "string", "description": "Relative path within GITHUB_WORKSPACE to place the checkout. Defaults to the workspace root.", - "examples": [".", "./libs/other-repo", "./workspace"] + "examples": [ + ".", + "./libs/other-repo", + "./workspace" + ] }, "fetch-depth": { "type": "integer", "minimum": 0, "description": "Number of commits to fetch. 0 fetches all history. 1 (default) is a shallow clone. When multiple configs target the same path, the deepest value is used.", - "examples": [0, 1, 10] + "examples": [ + 0, + 1, + 10 + ] }, "sparse-checkout": { "type": "string", "description": "Enable sparse-checkout with newline-separated patterns. When multiple configs target the same path, patterns are merged.", - "examples": [".github/\nsrc/", "docs/"] + "examples": [ + ".github/\nsrc/", + "docs/" + ] }, "submodules": { "oneOf": [ { "type": "string", - "enum": ["recursive", "true", "false"] + "enum": [ + "recursive", + "true", + "false" + ] }, { "type": "boolean" @@ -11413,12 +13051,18 @@ "token": { "type": "string", "description": "Deprecated: Use github-token instead. GitHub token for authentication. Credentials are always removed after checkout (persist-credentials: false is enforced).", - "examples": ["${{ secrets.MY_PAT }}", "${{ secrets.GITHUB_TOKEN }}"] + "examples": [ + "${{ secrets.MY_PAT }}", + "${{ secrets.GITHUB_TOKEN }}" + ] }, "github-token": { "type": "string", "description": "GitHub token for authentication. Use ${{ secrets.MY_TOKEN }} to reference a secret. Mutually exclusive with github-app (and deprecated app). Credentials are always removed after checkout (persist-credentials: false is enforced).", - "examples": ["${{ secrets.MY_PAT }}", "${{ secrets.CROSS_REPO_PAT }}"] + "examples": [ + "${{ secrets.MY_PAT }}", + "${{ secrets.CROSS_REPO_PAT }}" + ] }, "github-app": { "$ref": "#/$defs/github_app", @@ -11443,17 +13087,37 @@ } ], "description": "Additional Git refs to fetch after the checkout. Supported values: \"*\" (all branches), \"refs/pulls/open/*\" (all open pull-request refs), branch names (e.g. \"main\"), or glob patterns (e.g. \"feature/*\").", - "examples": [["*"], ["refs/pulls/open/*"], ["main", "feature/my-branch"], ["feature/*"]] + "examples": [ + [ + "*" + ], + [ + "refs/pulls/open/*" + ], + [ + "main", + "feature/my-branch" + ], + [ + "feature/*" + ] + ] }, "wiki": { "type": "boolean", "description": "When true, clones the repository's wiki git instead of the regular repository. The effective repository becomes \"{repository}.wiki\" (e.g. \"owner/repo.wiki\"). Defaults to false.", - "examples": [true, false] + "examples": [ + true, + false + ] }, "force-clean-git-credentials": { "type": "boolean", "description": "When true, persist credentials during checkout, then immediately run a post-checkout cleanup step that removes credentials from root and submodule git configs. Useful for submodule-safe cleanup behavior.", - "examples": [true, false] + "examples": [ + true, + false + ] } } }, @@ -11464,97 +13128,169 @@ "properties": { "actions": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for GitHub Actions workflows and runs (read: view workflows, write: manage workflows, none: no access)" }, "attestations": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for artifact attestations (read: view attestations, write: create attestations, none: no access)" }, "checks": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository checks and status checks (read: view checks, write: create/update checks, none: no access)" }, "contents": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository contents (read: view files, write: modify files/branches, none: no access)" }, "deployments": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository deployments (read: view deployments, write: create/update deployments, none: no access)" }, "discussions": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository discussions (read: view discussions, write: create/update discussions, none: no access)" }, "id-token": { "type": "string", - "enum": ["write", "none"], + "enum": [ + "write", + "none" + ], "description": "Permission level for OIDC token requests (write/none only - read is not supported). Allows workflows to request JWT tokens for cloud provider authentication." }, "issues": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository issues (read: view issues, write: create/update/close issues, none: no access)" }, "models": { "type": "string", - "enum": ["read", "none"], + "enum": [ + "read", + "none" + ], "description": "Permission for GitHub Copilot models (read: access AI models for agentic workflows, none: no access)" }, "metadata": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission for repository metadata (read: view repository information, write: update repository metadata, none: no access)" }, "packages": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for GitHub Packages (read/write/none). Controls access to publish, modify, or delete packages." }, "pages": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for GitHub Pages (read/write/none). Controls access to deploy and manage GitHub Pages sites." }, "pull-requests": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for pull requests (read/write/none). Controls access to create, edit, review, and manage pull requests." }, "repository-projects": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for repository projects (read/write/none). Controls access to manage repository-level GitHub Projects boards." }, "organization-projects": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for organization projects (read/write/none). Controls access to manage organization-level GitHub Projects boards." }, "security-events": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for security events (read/write/none). Controls access to view and manage code scanning alerts and security findings." }, "statuses": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for commit statuses (read/write/none). Controls access to create and update commit status checks." }, "vulnerability-alerts": { "type": "string", - "enum": ["read", "write", "none"], + "enum": [ + "read", + "write", + "none" + ], "description": "Permission level for Dependabot vulnerability alerts (read/write/none). Allows workflows to access the Dependabot alerts API via GITHUB_TOKEN instead of requiring a PAT or GitHub App." }, "all": { "type": "string", - "enum": ["read"], + "enum": [ + "read" + ], "description": "Permission shorthand that applies read access to all permission scopes. Can be combined with specific write permissions to override individual scopes. 'write' is not allowed for all." } } @@ -11566,152 +13302,271 @@ "properties": { "administration": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for repository administration (read/none; \"write\" is rejected by the compiler). GitHub App-only permission for repository administration." }, "codespaces": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for Codespaces (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "codespaces-lifecycle-admin": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for Codespaces lifecycle administration (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "codespaces-metadata": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for Codespaces metadata (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "email-addresses": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for user email addresses (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "environments": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for repository environments (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "git-signing": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for git signing (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "members": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization members (read/none; \"write\" is rejected by the compiler). Required for org team membership API calls." }, "organization-administration": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization administration (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-announcement-banners": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization announcement banners (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-codespaces": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization Codespaces (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-copilot": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization Copilot (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-custom-org-roles": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization custom org roles (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-custom-properties": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization custom properties (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-custom-repository-roles": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization custom repository roles (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-events": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization events (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-hooks": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization webhooks (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-members": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization members management (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-packages": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization packages (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-personal-access-token-requests": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization personal access token requests (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-personal-access-tokens": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization personal access tokens (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-plan": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization plan (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-self-hosted-runners": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization self-hosted runners (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "organization-user-blocking": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for organization user blocking (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "repository-custom-properties": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for repository custom properties (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "repository-hooks": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for repository webhooks (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "single-file": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for single file access (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "team-discussions": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for team discussions (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." }, "vulnerability-alerts": { "type": "string", - "enum": ["read", "none"], + "enum": [ + "read", + "none" + ], "description": "Permission level for Dependabot vulnerability alerts (read/none; \"write\" is rejected by the compiler). Also available as a GITHUB_TOKEN scope. When used with a GitHub App, forwarded as permission-vulnerability-alerts input." }, "workflows": { "type": "string", - "enum": ["read", "none", "write"], + "enum": [ + "read", + "none", + "write" + ], "description": "Permission level for GitHub Actions workflow files (read/none; \"write\" is rejected by the compiler). GitHub App-only permission." } }, diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 73f9edbe492..f1eaf285c98 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -183,10 +183,17 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str // no meaningful security boundary. In that case we switch back to // "bypassPermissions" which auto-approves all permission requests and produces a // smoother headless execution experience. - permissionMode := "acceptEdits" - if hasBashWildcardInTools(workflowData.Tools) { + // + // An explicit engine.permission-mode config field overrides both defaults. + var permissionMode string + if workflowData.EngineConfig != nil && workflowData.EngineConfig.PermissionMode != "" { + permissionMode = workflowData.EngineConfig.PermissionMode + claudeLog.Printf("Using explicit permission mode from config: %s", permissionMode) + } else if hasBashWildcardInTools(workflowData.Tools) { claudeLog.Print("Unrestricted bash detected: using bypassPermissions mode") permissionMode = "bypassPermissions" + } else { + permissionMode = "acceptEdits" } claudeArgs = append(claudeArgs, "--permission-mode", permissionMode) diff --git a/pkg/workflow/claude_engine_test.go b/pkg/workflow/claude_engine_test.go index 1169b287537..c1f5943d678 100644 --- a/pkg/workflow/claude_engine_test.go +++ b/pkg/workflow/claude_engine_test.go @@ -268,6 +268,60 @@ func TestClaudeEnginePermissionMode(t *testing.T) { } } +func TestClaudeEnginePermissionModeExplicitOverride(t *testing.T) { + engine := NewClaudeEngine() + + tests := []struct { + name string + permMode string + tools map[string]any + expectedMode string + }{ + { + // explicit auto overrides the bash-wildcard default + name: "explicit auto with bash wildcard", + permMode: "auto", + tools: map[string]any{"bash": []any{"*"}}, + expectedMode: "auto", + }, + { + // explicit acceptEdits overrides the bash-wildcard default + name: "explicit acceptEdits with bash wildcard", + permMode: "acceptEdits", + tools: map[string]any{"bash": []any{"*"}}, + expectedMode: "acceptEdits", + }, + { + // explicit bypassPermissions overrides the non-wildcard default + name: "explicit bypassPermissions with restricted bash", + permMode: "bypassPermissions", + tools: map[string]any{"bash": []any{"git", "echo"}}, + expectedMode: "bypassPermissions", + }, + { + name: "explicit plan mode", + permMode: "plan", + tools: nil, + expectedMode: "plan", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-workflow", + Tools: tt.tools, + EngineConfig: &EngineConfig{ID: "claude", PermissionMode: tt.permMode}, + } + steps := engine.GetExecutionSteps(workflowData, "test-log") + require.Len(t, steps, 1, "Expected one execution step") + stepContent := strings.Join([]string(steps[0]), "\n") + assert.Contains(t, stepContent, "--permission-mode "+tt.expectedMode, + "Expected --permission-mode %s", tt.expectedMode) + }) + } +} + func TestClaudeEngineConfiguration(t *testing.T) { engine := NewClaudeEngine() diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index 16fd85371b5..3e14bf7eca0 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -123,6 +123,11 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error) return nil, fmt.Errorf("%s: %w", cleanPath, err) } + // Validate optional engine.permission-mode configuration (Claude-specific). + if err := c.validateEnginePermissionMode(workflowData); err != nil { + return nil, fmt.Errorf("%s: %w", cleanPath, err) + } + // Validate that inlined-imports is not used with agent file imports. // Agent files require runtime access and cannot be resolved without sources. if workflowData.InlinedImports && engineSetup.importsResult.AgentFile != "" { diff --git a/pkg/workflow/compiler_string_api.go b/pkg/workflow/compiler_string_api.go index 7de6fe7caa7..8fe089488be 100644 --- a/pkg/workflow/compiler_string_api.go +++ b/pkg/workflow/compiler_string_api.go @@ -157,6 +157,11 @@ func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*Wor return nil, fmt.Errorf("%s: %w", cleanPath, err) } + // Validate optional engine.permission-mode configuration (Claude-specific). + if err := c.validateEnginePermissionMode(workflowData); err != nil { + return nil, fmt.Errorf("%s: %w", cleanPath, err) + } + // Validate GitHub tool configuration if err := validateGitHubToolConfig(workflowData.ParsedTools, workflowData.Name); err != nil { return nil, fmt.Errorf("%s: %w", cleanPath, err) diff --git a/pkg/workflow/engine.go b/pkg/workflow/engine.go index 45cf0cb8bad..0371278feeb 100644 --- a/pkg/workflow/engine.go +++ b/pkg/workflow/engine.go @@ -45,6 +45,7 @@ type EngineConfig struct { Agent string // Agent identifier for copilot --agent flag (copilot engine only) APITarget string // Custom API endpoint hostname (e.g., "api.acme.ghe.com" or "api.enterprise.githubcopilot.com") Bare bool // When true, disables automatic loading of context/instructions (copilot: --no-custom-instructions, claude: --bare, codex: --no-system-prompt, gemini: GEMINI_SYSTEM_MD=/dev/null) + PermissionMode string // Claude-only: explicit --permission-mode value (auto | acceptEdits | plan | bypassPermissions). Empty means auto-select based on tool configuration. // TokenWeights provides custom model cost data for effective token computation. // When set, overrides or extends the built-in model_multipliers.json values. TokenWeights *types.TokenWeights @@ -256,6 +257,15 @@ func (c *Compiler) ExtractEngineConfig(frontmatter map[string]any) (string, *Eng engineLog.Printf("Extracted bare mode (inline): %v", config.Bare) } } + + // Extract optional 'permission-mode' field (shared with non-inline path) + if pm, hasPM := engineObj["permission-mode"]; hasPM { + if pmStr, ok := pm.(string); ok && pmStr != "" { + config.PermissionMode = pmStr + engineLog.Printf("Extracted permission-mode (inline): %s", config.PermissionMode) + } + } + config.MaxRuns = topLevelMaxRuns config.MaxEffectiveTokens = topLevelMaxEffectiveTokens @@ -421,6 +431,14 @@ func (c *Compiler) ExtractEngineConfig(frontmatter map[string]any) (string, *Eng } } + // Extract optional 'permission-mode' field (Claude-only: explicit permission mode override) + if pm, hasPM := engineObj["permission-mode"]; hasPM { + if pmStr, ok := pm.(string); ok && pmStr != "" { + config.PermissionMode = pmStr + engineLog.Printf("Extracted permission-mode: %s", config.PermissionMode) + } + } + // Extract optional 'token-weights' field (custom model cost data) if tokenWeightsRaw, hasTokenWeights := engineObj["token-weights"]; hasTokenWeights { if tw := parseEngineTokenWeights(tokenWeightsRaw); tw != nil { diff --git a/pkg/workflow/engine_config_test.go b/pkg/workflow/engine_config_test.go index fb4d883626e..e2ca6c429a3 100644 --- a/pkg/workflow/engine_config_test.go +++ b/pkg/workflow/engine_config_test.go @@ -1135,3 +1135,75 @@ func TestEngineMCPToolTimeoutExtraction(t *testing.T) { }) } } + +func TestEnginePermissionModeExtraction(t *testing.T) { +compiler := NewCompiler() + +tests := []struct { +name string +frontmatter map[string]any +expectedMode string +}{ +{ +name: "permission-mode auto", +frontmatter: map[string]any{ +"engine": map[string]any{ +"id": "claude", +"permission-mode": "auto", +}, +}, +expectedMode: "auto", +}, +{ +name: "permission-mode acceptEdits", +frontmatter: map[string]any{ +"engine": map[string]any{ +"id": "claude", +"permission-mode": "acceptEdits", +}, +}, +expectedMode: "acceptEdits", +}, +{ +name: "permission-mode plan", +frontmatter: map[string]any{ +"engine": map[string]any{ +"id": "claude", +"permission-mode": "plan", +}, +}, +expectedMode: "plan", +}, +{ +name: "permission-mode bypassPermissions", +frontmatter: map[string]any{ +"engine": map[string]any{ +"id": "claude", +"permission-mode": "bypassPermissions", +}, +}, +expectedMode: "bypassPermissions", +}, +{ +name: "permission-mode not set — empty string", +frontmatter: map[string]any{ +"engine": map[string]any{ +"id": "claude", +}, +}, +expectedMode: "", +}, +} + +for _, tt := range tests { +t.Run(tt.name, func(t *testing.T) { +_, config := compiler.ExtractEngineConfig(tt.frontmatter) +if config == nil { +t.Fatal("Expected non-nil config") +} +if config.PermissionMode != tt.expectedMode { +t.Errorf("PermissionMode = %q, want %q", config.PermissionMode, tt.expectedMode) +} +}) +} +} diff --git a/pkg/workflow/engine_validation.go b/pkg/workflow/engine_validation.go index 20287821c59..6166e0bb4fa 100644 --- a/pkg/workflow/engine_validation.go +++ b/pkg/workflow/engine_validation.go @@ -153,6 +153,26 @@ func (c *Compiler) validateEngineMCPToolTimeout(workflowData *WorkflowData) erro return nil } +// validateEnginePermissionMode validates optional engine.permission-mode configuration. +// Valid values are: auto, acceptEdits, plan, bypassPermissions (Claude Code CLI values). +// The field is only meaningful for the Claude engine; it is silently ignored by other engines. +func (c *Compiler) validateEnginePermissionMode(workflowData *WorkflowData) error { + if workflowData == nil || workflowData.EngineConfig == nil || workflowData.EngineConfig.PermissionMode == "" { + return nil + } + + mode := workflowData.EngineConfig.PermissionMode + validModes := []string{"auto", "acceptEdits", "plan", "bypassPermissions"} + for _, v := range validModes { + if mode == v { + engineValidationLog.Printf("engine.permission-mode validated: %s", mode) + return nil + } + } + + return fmt.Errorf("engine.permission-mode: invalid value %q. Must be one of: auto, acceptEdits, plan, bypassPermissions.\n\nExample:\n engine:\n id: claude\n permission-mode: auto\n\nSee: %s", mode, constants.DocsEnginesURL) +} + // validateEngineInlineDefinition validates an inline engine definition parsed from // engine.runtime + optional engine.provider in the workflow frontmatter. // Returns an error if: diff --git a/pkg/workflow/engine_validation_test.go b/pkg/workflow/engine_validation_test.go index 3600e9ae925..d2ee1bef83a 100644 --- a/pkg/workflow/engine_validation_test.go +++ b/pkg/workflow/engine_validation_test.go @@ -671,3 +671,92 @@ func TestValidateEngineMCPToolTimeout(t *testing.T) { }) } } + +// TestValidateEnginePermissionMode tests the validateEnginePermissionMode function. +func TestValidateEnginePermissionMode(t *testing.T) { +tests := []struct { +name string +workflow *WorkflowData +expectError bool +errorSubstr string +}{ +{ +name: "nil workflow data", +workflow: nil, +expectError: false, +}, +{ +name: "nil engine config", +workflow: &WorkflowData{}, +expectError: false, +}, +{ +name: "permission-mode not set — no error", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude"}, +}, +expectError: false, +}, +{ +name: "valid mode: auto", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude", PermissionMode: "auto"}, +}, +expectError: false, +}, +{ +name: "valid mode: acceptEdits", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude", PermissionMode: "acceptEdits"}, +}, +expectError: false, +}, +{ +name: "valid mode: plan", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude", PermissionMode: "plan"}, +}, +expectError: false, +}, +{ +name: "valid mode: bypassPermissions", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude", PermissionMode: "bypassPermissions"}, +}, +expectError: false, +}, +{ +name: "invalid mode: unknown value", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude", PermissionMode: "superuser"}, +}, +expectError: true, +errorSubstr: "invalid value", +}, +{ +name: "invalid mode: case-mismatch", +workflow: &WorkflowData{ +EngineConfig: &EngineConfig{ID: "claude", PermissionMode: "bypasspermissions"}, +}, +expectError: true, +errorSubstr: "invalid value", +}, +} + +for _, tt := range tests { +t.Run(tt.name, func(t *testing.T) { +compiler := NewCompiler() +err := compiler.validateEnginePermissionMode(tt.workflow) + +if tt.expectError { +require.Error(t, err, "Expected validation error") +if tt.errorSubstr != "" { +assert.Contains(t, err.Error(), tt.errorSubstr, "Expected error substring mismatch") +} +return +} + +assert.NoError(t, err, "Expected permission-mode validation to pass") +}) +} +}