diff --git a/.changeset/patch-kiro-release-smoke-diagnostics.md b/.changeset/patch-kiro-release-smoke-diagnostics.md new file mode 100644 index 00000000000..3de9e1a53e4 --- /dev/null +++ b/.changeset/patch-kiro-release-smoke-diagnostics.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Update the unsupported Kiro engine sample from CLI 2.16.1 to 2.27.1 with verified Linux release checksums. Add secret-safe MCP and harness lifecycle diagnostics, preserve MCP startup exit codes, and correct smoke-kiro to exercise native GitHub MCP with Go build network access. diff --git a/.github/workflows/engine-conformance-kiro.lock.yml b/.github/workflows/engine-conformance-kiro.lock.yml index 12fd6ce2033..5dcbce3754e 100644 --- a/.github/workflows/engine-conformance-kiro.lock.yml +++ b/.github/workflows/engine-conformance-kiro.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"1fcc7ae7fc05fef101920a5b61c196c846340d78dddeac16be1015b59aa21050","body_hash":"31180d534dfd591c2368af5271c01e7a5bf48baa46fc8f870f0247d11d1b2a28","strict":true,"agent_id":"kiro","agent_model":"kiro/auto"} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5a934165119b0abcbbbe0369efa275a87ca8d2025ca3c15de7768b5b713bc097","body_hash":"1e982562d16bce34d8e6d8c5b872b2be6f924079ec3ee50bb82baa529ca78e2f","strict":true,"agent_id":"kiro","agent_model":"kiro/auto","engine_versions":{"kiro":"2.27.1"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_DEFAULT_OTLP_ENDPOINT","GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","KIRO_API_KEY"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.31","digest":"sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.31@sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.31","digest":"sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.31@sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.31","digest":"sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.31@sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.28","digest":"sha256:69920365d9f0143ed853743c319b2bd4614ca7f9537474ad8eff0c47da84c5a4","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.28@sha256:69920365d9f0143ed853743c319b2bd4614ca7f9537474ad8eff0c47da84c5a4"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:11c2c544876cdbaffefe0f7f1283b74838c209654b4934a78e23ed11f750490f","pinned_image":"ghcr.io/github/gh-aw-node@sha256:11c2c544876cdbaffefe0f7f1283b74838c209654b4934a78e23ed11f750490f"},{"image":"ghcr.io/github/github-mcp-server:v1.12.2","digest":"sha256:508a0857ec762b1ab1cece29193345b501fab1dd9d1228a7b617062954cecac6","pinned_image":"ghcr.io/github/github-mcp-server:v1.12.2@sha256:508a0857ec762b1ab1cece29193345b501fab1dd9d1228a7b617062954cecac6"}],"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"mcpscripts","tools":["conformance-challenge"]},{"name":"safeoutputs","tools":["create_issue","missing_data","missing_tool","noop","report_incomplete"]}],"threat_detection":{"mode":"disabled"}} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -130,6 +130,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Engine Conformance Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/engine-conformance-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Mask OTLP telemetry headers @@ -140,8 +141,8 @@ jobs: GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_INFO_ENGINE_NAME: "Kiro" GH_AW_INFO_MODEL: "kiro/auto" - GH_AW_INFO_VERSION: "" - GH_AW_INFO_AGENT_VERSION: "" + GH_AW_INFO_VERSION: "2.27.1" + GH_AW_INFO_AGENT_VERSION: "2.27.1" GH_AW_INFO_WORKFLOW_NAME: "Engine Conformance Kiro" GH_AW_INFO_EXPERIMENTAL: "true" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "false" @@ -395,6 +396,7 @@ jobs: GH_AW_ASSETS_ALLOWED_EXTS: "" GH_AW_ASSETS_BRANCH: "" GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_ENGINE_VERSION: "2.27.1" GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs GH_AW_PROJECT_UTC: "-08:00" GH_AW_PR_HEAD_BASE_BRANCH: "" @@ -439,6 +441,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Engine Conformance Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/engine-conformance-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Set runtime paths @@ -813,6 +816,7 @@ jobs: cat <<'GHAW_MCP_CONFIG_ADAPTER_SCRIPT_7e1a4d2c_EOF' > "${RUNNER_TEMP}/gh-aw/actions/kiro_mcp_config_adapter.cjs" const fs = require("fs"); const path = require("path"); + const log = message => process.stderr.write(`[kiro-mcp] ${message}\n`); const requireEnvVar = name => { const value = process.env[name]; @@ -825,25 +829,47 @@ jobs: const gatewayDomain = process.env.MCP_GATEWAY_DOMAIN || "host.docker.internal"; const gatewayPort = requireEnvVar("MCP_GATEWAY_PORT"); const gatewayURL = `http://${gatewayDomain}:${gatewayPort}`; + log("Reading MCP gateway configuration"); let cliServers; try { - cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]")); - } catch (error) { - throw new Error(`Failed to parse GH_AW_MCP_CLI_SERVERS: ${error instanceof Error ? error.message : String(error)}`); + cliServers = JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"); + } catch { + throw new Error("GH_AW_MCP_CLI_SERVERS must be a JSON array of server names"); + } + if (!Array.isArray(cliServers) || !cliServers.every(name => typeof name === "string")) { + throw new Error("GH_AW_MCP_CLI_SERVERS must be a JSON array of server names"); } + cliServers = new Set(cliServers); - const gatewayOutput = JSON.parse(fs.readFileSync(gatewayOutputPath, "utf8")); - const rawServers = gatewayOutput.mcpServers; - const servers = rawServers && typeof rawServers === "object" && !Array.isArray(rawServers) ? rawServers : {}; + const gatewayContent = fs.readFileSync(gatewayOutputPath, "utf8"); + let gatewayOutput; + try { + gatewayOutput = JSON.parse(gatewayContent); + } catch { + throw new Error("MCP_GATEWAY_OUTPUT must contain valid JSON; check the MCP gateway logs"); + } + const servers = gatewayOutput?.mcpServers; + if (!servers || typeof servers !== "object" || Array.isArray(servers)) { + throw new Error("MCP_GATEWAY_OUTPUT must contain an mcpServers object"); + } const mcpServers = {}; + let skipped = 0; + let httpServers = 0; for (const [name, entry] of Object.entries(servers)) { - if (cliServers.has(name) || !entry || typeof entry !== "object") continue; + if (cliServers.has(name)) { + skipped++; + continue; + } + if (!entry || typeof entry !== "object" || Array.isArray(entry)) { + throw new Error("MCP_GATEWAY_OUTPUT contains an invalid server entry"); + } const transformed = { ...entry }; if (typeof transformed.url === "string") { transformed.url = transformed.url.replace(/^http:\/\/[^/]+\/mcp\//, `${gatewayURL}/mcp/`); transformed.type = "http"; + httpServers++; } delete transformed.tools; mcpServers[name] = transformed; @@ -853,6 +879,7 @@ jobs: fs.mkdirSync(path.dirname(configPath), { recursive: true }); fs.writeFileSync(configPath, JSON.stringify({ mcpServers }, null, 2), { mode: 0o600 }); fs.chmodSync(configPath, 0o600); + log(`Wrote ${Object.keys(mcpServers).length} native MCP server(s), ${httpServers} HTTP server(s), skipped ${skipped} CLI-mounted server(s); permissions=0600`); GHAW_MCP_CONFIG_ADAPTER_SCRIPT_7e1a4d2c_EOF chmod 755 "${RUNNER_TEMP}/gh-aw/actions/kiro_mcp_config_adapter.cjs" @@ -1033,62 +1060,104 @@ jobs: const [, ...commandArgs] = process.argv.slice(2); const installDir = mkdtempSync(join(tmpdir(), "kiro-cli-")); const archive = join(installDir, "kiro-cli.tar.gz"); - const version = "2.16.1"; + const version = "2.27.1"; + const started = Date.now(); const releases = { x64: { arch: "x86_64", - checksum: "393633c991faab5ef688b2aa0dd420481cab7fca312cd863923c3287e9874b82", + checksum: "3c0d7268a4bfb73f8e827822049978fa578e020b271afc1021c7532602d45d99", }, arm64: { arch: "aarch64", - checksum: "8413b62072780747374c0932147022e290725e934d461e430b5ae12a73f33b23", + checksum: "33ad5462c3111ba4ef527f1d58bda08a9d1997e0ae73841a7c2ca734ebcba2d0", }, }; const fail = (result, action) => { if (result.error) throw result.error; - if (result.status !== 0) throw new Error(`${action} failed with exit code ${result.status ?? "unknown"}`); + if (result.status !== 0) { + const detail = result.signal ? `signal ${result.signal}` : `exit code ${result.status ?? "unknown"}`; + const hint = action === "Kiro CLI execution" && result.status === 3 + ? "; required MCP server startup failed; check .kiro/settings/mcp.json and MCP gateway logs" + : ""; + const error = new Error(`${action} failed with ${detail}${hint}`); + error.exitCode = result.status ?? 1; + throw error; + } }; const log = message => process.stderr.write(`[kiro-harness] ${message}\n`); + const run = (command, args, action, options = {}) => { + const start = Date.now(); + log(`${action} started`); + fail(spawnSync(command, args, { stdio: "inherit", ...options }), action); + log(`${action} completed in ${Date.now() - start}ms`); + }; try { + log(`Starting Kiro CLI ${version}; architecture=${process.arch}`); + if (process.env.GH_AW_ENGINE_VERSION && process.env.GH_AW_ENGINE_VERSION !== version) { + throw new Error(`This Kiro harness supports only engine.version ${version}; update the pinned release and checksums together`); + } if (!process.env.KIRO_API_KEY && process.env.SECRET_KIRO_API_KEY) { process.env.KIRO_API_KEY = process.env.SECRET_KIRO_API_KEY; + log("Using KIRO_API_KEY from the secret binding"); } + if (!process.env.KIRO_API_KEY) throw new Error("KIRO_API_KEY is required for headless execution; configure the KIRO_API_KEY Actions secret"); + log("Headless API key is configured"); const release = releases[process.arch]; if (!release) throw new Error(`Unsupported Kiro CLI architecture: ${process.arch}`); - const releaseURL = `https://prod.download.cli.kiro.dev/stable/${version}/kirocli-${release.arch}-linux.tar.gz`; - - fail(spawnSync("curl", ["--fail", "--location", "--silent", "--show-error", "--output", archive, releaseURL], { stdio: "inherit" }), "Kiro CLI download"); - if (createHash("sha256").update(readFileSync(archive)).digest("hex") !== release.checksum) { - throw new Error("Kiro CLI download checksum did not match"); - } - fail(spawnSync("tar", ["-xzf", archive, "-C", installDir], { stdio: "inherit" }), "Kiro CLI extraction"); - - const binDir = join(installDir, "kirocli", "bin"); - const executable = join(binDir, "kiro-cli"); - if (!existsSync(executable)) throw new Error("Kiro CLI executable was not found in the release archive"); - fail(spawnSync(executable, ["--version"], { stdio: "inherit" }), "Kiro CLI verification"); const promptPath = process.env.GH_AW_PROMPT; if (!promptPath) throw new Error("GH_AW_PROMPT is required"); const prompt = readFileSync(promptPath, "utf8"); + if (!prompt.trim()) throw new Error("GH_AW_PROMPT must contain a non-empty instruction"); const selectedModel = process.env.KIRO_MODEL; if (!selectedModel?.startsWith("kiro/")) { throw new Error("KIRO_MODEL must use kiro/model format"); } const model = selectedModel.slice("kiro/".length); - if (!model) throw new Error("KIRO_MODEL must include a model name"); + if (!model.trim()) throw new Error("KIRO_MODEL must include a model name"); + log(`Prompt loaded (${Buffer.byteLength(prompt, "utf8")} bytes); model override is configured`); + + const mcpConfigPath = process.env.GH_AW_MCP_CONFIG; + if (mcpConfigPath) { + let config; + const content = readFileSync(mcpConfigPath, "utf8"); + try { + config = JSON.parse(content); + } catch { + throw new Error("GH_AW_MCP_CONFIG must contain valid JSON; check the Kiro MCP adapter logs"); + } + if (!config?.mcpServers || typeof config.mcpServers !== "object" || Array.isArray(config.mcpServers)) { + throw new Error("GH_AW_MCP_CONFIG must contain an mcpServers object"); + } + log(`Native MCP configuration loaded (${Object.keys(config.mcpServers).length} server(s))`); + } else { + log("No GH_AW_MCP_CONFIG path supplied; Kiro will use its default MCP configuration"); + } + + const releaseURL = `https://prod.download.cli.kiro.dev/stable/${version}/kirocli-${release.arch}-linux.tar.gz`; + run("curl", ["--fail", "--location", "--silent", "--show-error", "--output", archive, releaseURL], "Kiro CLI download"); + if (createHash("sha256").update(readFileSync(archive)).digest("hex") !== release.checksum) { + throw new Error("Kiro CLI download checksum did not match"); + } + log("Kiro CLI SHA-256 checksum verified"); + run("tar", ["-xzf", archive, "-C", installDir], "Kiro CLI extraction"); + + const binDir = join(installDir, "kirocli", "bin"); + const executable = join(binDir, "kiro-cli"); + if (!existsSync(executable)) throw new Error("Kiro CLI executable was not found in the release archive"); + run(executable, ["--version"], "Kiro CLI verification"); - fail(spawnSync(executable, [...commandArgs, "--model", model, prompt], { + run(executable, [...commandArgs, "--model", model, "--", prompt], "Kiro CLI execution", { cwd: process.env.GITHUB_WORKSPACE, env: { ...process.env, PATH: `${binDir}:${process.env.PATH || ""}` }, - stdio: "inherit", - }), "Kiro CLI execution"); + }); } catch (error) { log(error instanceof Error ? error.message : String(error)); - process.exitCode = 1; + process.exitCode = Number.isInteger(error?.exitCode) && error.exitCode > 0 ? error.exitCode : 1; } finally { rmSync(installDir, { recursive: true, force: true }); + log(`Cleaned up Kiro CLI installation; total duration=${Date.now() - started}ms; exit code=${process.exitCode || 0}`); } GHAW_HARNESS_SCRIPT_3c7b9f1a_EOF @@ -1132,6 +1201,7 @@ jobs: AWF_REFLECT_ENABLED: 1 CONFORMANCE_STATE: ${{ runner.temp }}/engine-conformance ENGINE_CONFORMANCE_SENTINEL: conformance-kiro + GH_AW_ENGINE_VERSION: 2.27.1 GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} GH_AW_MCP_CONFIG: .kiro/settings/mcp.json GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt @@ -1367,6 +1437,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Engine Conformance Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/engine-conformance-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Download agent output artifact @@ -1610,6 +1681,7 @@ jobs: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/engine-conformance-kiro" GH_AW_ENGINE_ID: "kiro" GH_AW_ENGINE_MODEL: "kiro/auto" + GH_AW_ENGINE_VERSION: "2.27.1" GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_SAFE_OUTPUTS_STAGED: "true" @@ -1653,6 +1725,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Engine Conformance Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/engine-conformance-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Mask OTLP telemetry headers diff --git a/.github/workflows/shared/kiro.md b/.github/workflows/shared/kiro.md index 6da7f8f05b3..51e4d4b1bd0 100644 --- a/.github/workflows/shared/kiro.md +++ b/.github/workflows/shared/kiro.md @@ -5,6 +5,7 @@ engine: display-name: Kiro description: Kiro CLI with headless execution and native MCP support experimental: true + version: "2.27.1" provider: name: kiro auth: @@ -54,6 +55,7 @@ engine: config-adapter: | const fs = require("fs"); const path = require("path"); + const log = message => process.stderr.write(`[kiro-mcp] ${message}\n`); const requireEnvVar = name => { const value = process.env[name]; @@ -66,25 +68,47 @@ engine: const gatewayDomain = process.env.MCP_GATEWAY_DOMAIN || "host.docker.internal"; const gatewayPort = requireEnvVar("MCP_GATEWAY_PORT"); const gatewayURL = `http://${gatewayDomain}:${gatewayPort}`; + log("Reading MCP gateway configuration"); let cliServers; try { - cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]")); - } catch (error) { - throw new Error(`Failed to parse GH_AW_MCP_CLI_SERVERS: ${error instanceof Error ? error.message : String(error)}`); + cliServers = JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"); + } catch { + throw new Error("GH_AW_MCP_CLI_SERVERS must be a JSON array of server names"); } + if (!Array.isArray(cliServers) || !cliServers.every(name => typeof name === "string")) { + throw new Error("GH_AW_MCP_CLI_SERVERS must be a JSON array of server names"); + } + cliServers = new Set(cliServers); - const gatewayOutput = JSON.parse(fs.readFileSync(gatewayOutputPath, "utf8")); - const rawServers = gatewayOutput.mcpServers; - const servers = rawServers && typeof rawServers === "object" && !Array.isArray(rawServers) ? rawServers : {}; + const gatewayContent = fs.readFileSync(gatewayOutputPath, "utf8"); + let gatewayOutput; + try { + gatewayOutput = JSON.parse(gatewayContent); + } catch { + throw new Error("MCP_GATEWAY_OUTPUT must contain valid JSON; check the MCP gateway logs"); + } + const servers = gatewayOutput?.mcpServers; + if (!servers || typeof servers !== "object" || Array.isArray(servers)) { + throw new Error("MCP_GATEWAY_OUTPUT must contain an mcpServers object"); + } const mcpServers = {}; + let skipped = 0; + let httpServers = 0; for (const [name, entry] of Object.entries(servers)) { - if (cliServers.has(name) || !entry || typeof entry !== "object") continue; + if (cliServers.has(name)) { + skipped++; + continue; + } + if (!entry || typeof entry !== "object" || Array.isArray(entry)) { + throw new Error("MCP_GATEWAY_OUTPUT contains an invalid server entry"); + } const transformed = { ...entry }; if (typeof transformed.url === "string") { transformed.url = transformed.url.replace(/^http:\/\/[^/]+\/mcp\//, `${gatewayURL}/mcp/`); transformed.type = "http"; + httpServers++; } delete transformed.tools; mcpServers[name] = transformed; @@ -94,6 +118,7 @@ engine: fs.mkdirSync(path.dirname(configPath), { recursive: true }); fs.writeFileSync(configPath, JSON.stringify({ mcpServers }, null, 2), { mode: 0o600 }); fs.chmodSync(configPath, 0o600); + log(`Wrote ${Object.keys(mcpServers).length} native MCP server(s), ${httpServers} HTTP server(s), skipped ${skipped} CLI-mounted server(s); permissions=0600`); harness-script: | const { createHash } = require("crypto"); const { existsSync, mkdtempSync, readFileSync, rmSync } = require("fs"); @@ -104,62 +129,104 @@ engine: const [, ...commandArgs] = process.argv.slice(2); const installDir = mkdtempSync(join(tmpdir(), "kiro-cli-")); const archive = join(installDir, "kiro-cli.tar.gz"); - const version = "2.16.1"; + const version = "2.27.1"; + const started = Date.now(); const releases = { x64: { arch: "x86_64", - checksum: "393633c991faab5ef688b2aa0dd420481cab7fca312cd863923c3287e9874b82", + checksum: "3c0d7268a4bfb73f8e827822049978fa578e020b271afc1021c7532602d45d99", }, arm64: { arch: "aarch64", - checksum: "8413b62072780747374c0932147022e290725e934d461e430b5ae12a73f33b23", + checksum: "33ad5462c3111ba4ef527f1d58bda08a9d1997e0ae73841a7c2ca734ebcba2d0", }, }; const fail = (result, action) => { if (result.error) throw result.error; - if (result.status !== 0) throw new Error(`${action} failed with exit code ${result.status ?? "unknown"}`); + if (result.status !== 0) { + const detail = result.signal ? `signal ${result.signal}` : `exit code ${result.status ?? "unknown"}`; + const hint = action === "Kiro CLI execution" && result.status === 3 + ? "; required MCP server startup failed; check .kiro/settings/mcp.json and MCP gateway logs" + : ""; + const error = new Error(`${action} failed with ${detail}${hint}`); + error.exitCode = result.status ?? 1; + throw error; + } }; const log = message => process.stderr.write(`[kiro-harness] ${message}\n`); + const run = (command, args, action, options = {}) => { + const start = Date.now(); + log(`${action} started`); + fail(spawnSync(command, args, { stdio: "inherit", ...options }), action); + log(`${action} completed in ${Date.now() - start}ms`); + }; try { + log(`Starting Kiro CLI ${version}; architecture=${process.arch}`); + if (process.env.GH_AW_ENGINE_VERSION && process.env.GH_AW_ENGINE_VERSION !== version) { + throw new Error(`This Kiro harness supports only engine.version ${version}; update the pinned release and checksums together`); + } if (!process.env.KIRO_API_KEY && process.env.SECRET_KIRO_API_KEY) { process.env.KIRO_API_KEY = process.env.SECRET_KIRO_API_KEY; + log("Using KIRO_API_KEY from the secret binding"); } + if (!process.env.KIRO_API_KEY) throw new Error("KIRO_API_KEY is required for headless execution; configure the KIRO_API_KEY Actions secret"); + log("Headless API key is configured"); const release = releases[process.arch]; if (!release) throw new Error(`Unsupported Kiro CLI architecture: ${process.arch}`); - const releaseURL = `https://prod.download.cli.kiro.dev/stable/${version}/kirocli-${release.arch}-linux.tar.gz`; - - fail(spawnSync("curl", ["--fail", "--location", "--silent", "--show-error", "--output", archive, releaseURL], { stdio: "inherit" }), "Kiro CLI download"); - if (createHash("sha256").update(readFileSync(archive)).digest("hex") !== release.checksum) { - throw new Error("Kiro CLI download checksum did not match"); - } - fail(spawnSync("tar", ["-xzf", archive, "-C", installDir], { stdio: "inherit" }), "Kiro CLI extraction"); - - const binDir = join(installDir, "kirocli", "bin"); - const executable = join(binDir, "kiro-cli"); - if (!existsSync(executable)) throw new Error("Kiro CLI executable was not found in the release archive"); - fail(spawnSync(executable, ["--version"], { stdio: "inherit" }), "Kiro CLI verification"); const promptPath = process.env.GH_AW_PROMPT; if (!promptPath) throw new Error("GH_AW_PROMPT is required"); const prompt = readFileSync(promptPath, "utf8"); + if (!prompt.trim()) throw new Error("GH_AW_PROMPT must contain a non-empty instruction"); const selectedModel = process.env.KIRO_MODEL; if (!selectedModel?.startsWith("kiro/")) { throw new Error("KIRO_MODEL must use kiro/model format"); } const model = selectedModel.slice("kiro/".length); - if (!model) throw new Error("KIRO_MODEL must include a model name"); + if (!model.trim()) throw new Error("KIRO_MODEL must include a model name"); + log(`Prompt loaded (${Buffer.byteLength(prompt, "utf8")} bytes); model override is configured`); + + const mcpConfigPath = process.env.GH_AW_MCP_CONFIG; + if (mcpConfigPath) { + let config; + const content = readFileSync(mcpConfigPath, "utf8"); + try { + config = JSON.parse(content); + } catch { + throw new Error("GH_AW_MCP_CONFIG must contain valid JSON; check the Kiro MCP adapter logs"); + } + if (!config?.mcpServers || typeof config.mcpServers !== "object" || Array.isArray(config.mcpServers)) { + throw new Error("GH_AW_MCP_CONFIG must contain an mcpServers object"); + } + log(`Native MCP configuration loaded (${Object.keys(config.mcpServers).length} server(s))`); + } else { + log("No GH_AW_MCP_CONFIG path supplied; Kiro will use its default MCP configuration"); + } - fail(spawnSync(executable, [...commandArgs, "--model", model, prompt], { + const releaseURL = `https://prod.download.cli.kiro.dev/stable/${version}/kirocli-${release.arch}-linux.tar.gz`; + run("curl", ["--fail", "--location", "--silent", "--show-error", "--output", archive, releaseURL], "Kiro CLI download"); + if (createHash("sha256").update(readFileSync(archive)).digest("hex") !== release.checksum) { + throw new Error("Kiro CLI download checksum did not match"); + } + log("Kiro CLI SHA-256 checksum verified"); + run("tar", ["-xzf", archive, "-C", installDir], "Kiro CLI extraction"); + + const binDir = join(installDir, "kirocli", "bin"); + const executable = join(binDir, "kiro-cli"); + if (!existsSync(executable)) throw new Error("Kiro CLI executable was not found in the release archive"); + run(executable, ["--version"], "Kiro CLI verification"); + + run(executable, [...commandArgs, "--model", model, "--", prompt], "Kiro CLI execution", { cwd: process.env.GITHUB_WORKSPACE, env: { ...process.env, PATH: `${binDir}:${process.env.PATH || ""}` }, - stdio: "inherit", - }), "Kiro CLI execution"); + }); } catch (error) { log(error instanceof Error ? error.message : String(error)); - process.exitCode = 1; + process.exitCode = Number.isInteger(error?.exitCode) && error.exitCode > 0 ? error.exitCode : 1; } finally { rmSync(installDir, { recursive: true, force: true }); + log(`Cleaned up Kiro CLI installation; total duration=${Date.now() - started}ms; exit code=${process.exitCode || 0}`); } --- @@ -180,4 +247,25 @@ imports: Configure the `KIRO_API_KEY` GitHub Actions secret with an API key from Kiro. Kiro serves the selected model through its own API, so this engine does not use universal provider routing. + +The harness pins [Kiro CLI 2.27.1](https://kiro.dev/changelog/cli/2-27/) and +verifies the x86_64 or aarch64 Linux archive against the SHA-256 checksum in +the [stable release manifest](https://prod.download.cli.kiro.dev/stable/latest/manifest.json). +Update `engine.version`, the harness version, and both checksums together. +Other `engine.version` overrides are rejected rather than silently installing +a different release. + +Headless execution retains `--no-interactive`, `--trust-all-tools`, and +`--require-mcp-startup`; `kiro/` is passed as `--model `. +Native MCP configuration uses `.kiro/settings/mcp.json`, preserves HTTP +authorization headers, and excludes servers already mounted as CLIs. +The config is written with owner-only permissions. MCP startup failures retain +Kiro's exit code 3, with a hint to inspect the gateway logs. + +Look for `[kiro-mcp]` and `[kiro-harness]` in the agent logs for MCP server +counts, authentication presence, prompt size, installation timings, checksum +verification, process failures, and cleanup. These diagnostics do not print +API keys, MCP headers, configuration contents, or prompt contents. For Kiro's +own verbose CLI logging, add `--verbose` to `behaviors.execution.args` in this +shared definition when investigating a runtime failure. --> diff --git a/.github/workflows/smoke-kiro.lock.yml b/.github/workflows/smoke-kiro.lock.yml index dc36fb7549b..7de4430b49e 100644 --- a/.github/workflows/smoke-kiro.lock.yml +++ b/.github/workflows/smoke-kiro.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"60a76120ff04d4ad2cccef2e78ea9d8ac157d84bc16c82d393211a15d1ba23f6","body_hash":"798c25d9b19262df6d80454c69e35abf2ab4fac53645a7cecff23866421773ea","strict":true,"agent_id":"kiro","agent_model":"kiro/auto"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","KIRO_API_KEY"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.31","digest":"sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.31@sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.31","digest":"sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.31@sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.31","digest":"sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.31@sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.28","digest":"sha256:69920365d9f0143ed853743c319b2bd4614ca7f9537474ad8eff0c47da84c5a4","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.28@sha256:69920365d9f0143ed853743c319b2bd4614ca7f9537474ad8eff0c47da84c5a4"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:11c2c544876cdbaffefe0f7f1283b74838c209654b4934a78e23ed11f750490f","pinned_image":"ghcr.io/github/gh-aw-node@sha256:11c2c544876cdbaffefe0f7f1283b74838c209654b4934a78e23ed11f750490f"},{"image":"ghcr.io/github/github-mcp-server:v1.12.2","digest":"sha256:508a0857ec762b1ab1cece29193345b501fab1dd9d1228a7b617062954cecac6","pinned_image":"ghcr.io/github/github-mcp-server:v1.12.2@sha256:508a0857ec762b1ab1cece29193345b501fab1dd9d1228a7b617062954cecac6"}],"has_pull_request":true,"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["add_comment","add_labels","create_issue","missing_data","missing_tool","noop","report_incomplete"]}],"threat_detection":{"mode":"enabled"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"48f64c79b01539aa836149cbd6c7c9835b813fe4a54af8f2460cdc754c27800f","body_hash":"9eecb30b76e0445d34547eeffd6eb2ab095d115b9096583103c5c6d32feec283","strict":true,"agent_id":"kiro","agent_model":"kiro/auto","engine_versions":{"kiro":"2.27.1"}} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","KIRO_API_KEY"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.31","digest":"sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.31@sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.31","digest":"sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.31@sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.31","digest":"sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.31@sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.28","digest":"sha256:69920365d9f0143ed853743c319b2bd4614ca7f9537474ad8eff0c47da84c5a4","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.28@sha256:69920365d9f0143ed853743c319b2bd4614ca7f9537474ad8eff0c47da84c5a4"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:11c2c544876cdbaffefe0f7f1283b74838c209654b4934a78e23ed11f750490f","pinned_image":"ghcr.io/github/gh-aw-node@sha256:11c2c544876cdbaffefe0f7f1283b74838c209654b4934a78e23ed11f750490f"},{"image":"ghcr.io/github/github-mcp-server:v1.12.2","digest":"sha256:508a0857ec762b1ab1cece29193345b501fab1dd9d1228a7b617062954cecac6","pinned_image":"ghcr.io/github/github-mcp-server:v1.12.2@sha256:508a0857ec762b1ab1cece29193345b501fab1dd9d1228a7b617062954cecac6"}],"has_pull_request":true,"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["add_comment","add_labels","create_issue","missing_data","missing_tool","noop","report_incomplete"]}],"threat_detection":{"mode":"enabled"}} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -28,7 +28,6 @@ # Resolved workflow manifest: # Imports: # - shared/kiro.md -# - shared/gh.md # - shared/reporting.md # - shared/otlp.md # - shared/reporting-otlp.md @@ -51,6 +50,7 @@ # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) +# - actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 # - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 # @@ -157,6 +157,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Mask OTLP telemetry headers @@ -167,13 +168,13 @@ jobs: GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_INFO_ENGINE_NAME: "Kiro" GH_AW_INFO_MODEL: "kiro/auto" - GH_AW_INFO_VERSION: "" - GH_AW_INFO_AGENT_VERSION: "" + GH_AW_INFO_VERSION: "2.27.1" + GH_AW_INFO_AGENT_VERSION: "2.27.1" GH_AW_INFO_WORKFLOW_NAME: "Smoke Kiro" GH_AW_INFO_EXPERIMENTAL: "true" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "false" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["*.grafana.net","*.sentry.io","defaults","github"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["*.grafana.net","*.sentry.io","defaults","github","go"]' GH_AW_INFO_FIREWALL_ENABLED: "false" GH_AW_INFO_AWF_VERSION: "" GH_AW_INFO_AWMG_VERSION: "" @@ -307,7 +308,7 @@ jobs: id: sanitized uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" with: script: | const path = require('path'); @@ -341,7 +342,7 @@ jobs: GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl - GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_mcp_transport_prompt.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"file\":\"pr_context_prompt.md\",\"condition_env\":\"GH_AW_INCLUDE_PR_CONTEXT\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0006\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0007\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0008\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0009\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0010\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0011\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0012\"}],\"system_item_count\":13}" + GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_mcp_transport_prompt.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"file\":\"pr_context_prompt.md\",\"condition_env\":\"GH_AW_INCLUDE_PR_CONTEXT\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0006\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0007\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0008\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0009\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0010\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0011\"}],\"system_item_count\":13}" GH_AW_EXPR_76DF9333: ${{ github.event.pull_request.number || (fromJSON(steps.generate_aw_info.outputs.aw_context || '{}').item_type == 'pull_request' && fromJSON(steps.generate_aw_info.outputs.aw_context || '{}').item_number) }} GH_AW_EXPR_77C1A4D2: ${{ github.event.comment.id || fromJSON(steps.generate_aw_info.outputs.aw_context || '{}').comment_id }} GH_AW_EXPR_7C248226: ${{ github.event.issue.number || (fromJSON(steps.generate_aw_info.outputs.aw_context || '{}').item_type == 'issue' && fromJSON(steps.generate_aw_info.outputs.aw_context || '{}').item_number) }} @@ -358,13 +359,12 @@ jobs: GH_AW_PROMPT_CONTENT_0003: "\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_7C248226__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_C19C384F__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_76DF9333__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_77C1A4D2__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n\n\n" GH_AW_PROMPT_CONTENT_0004: "\n" GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/shared/kiro.md}}\n" - GH_AW_PROMPT_CONTENT_0006: "{{#runtime-import .github/workflows/shared/gh.md}}\n" - GH_AW_PROMPT_CONTENT_0007: "{{#runtime-import .github/workflows/shared/reporting-otlp.md}}\n" - GH_AW_PROMPT_CONTENT_0008: "{{#runtime-import .github/workflows/shared/otlp.md}}\n" - GH_AW_PROMPT_CONTENT_0009: "{{#runtime-import .github/workflows/shared/token-telemetry-check.md}}\n" - GH_AW_PROMPT_CONTENT_0010: "{{#runtime-import .github/workflows/shared/smoke-test-brevity.md}}\n" - GH_AW_PROMPT_CONTENT_0011: "{{#runtime-import .github/workflows/shared/reporting.md}}\n" - GH_AW_PROMPT_CONTENT_0012: "{{#runtime-import .github/workflows/smoke-kiro.md}}\n" + GH_AW_PROMPT_CONTENT_0006: "{{#runtime-import .github/workflows/shared/reporting-otlp.md}}\n" + GH_AW_PROMPT_CONTENT_0007: "{{#runtime-import .github/workflows/shared/otlp.md}}\n" + GH_AW_PROMPT_CONTENT_0008: "{{#runtime-import .github/workflows/shared/token-telemetry-check.md}}\n" + GH_AW_PROMPT_CONTENT_0009: "{{#runtime-import .github/workflows/shared/smoke-test-brevity.md}}\n" + GH_AW_PROMPT_CONTENT_0010: "{{#runtime-import .github/workflows/shared/reporting.md}}\n" + GH_AW_PROMPT_CONTENT_0011: "{{#runtime-import .github/workflows/smoke-kiro.md}}\n" with: script: | const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); @@ -488,6 +488,7 @@ jobs: GH_AW_ASSETS_ALLOWED_EXTS: "" GH_AW_ASSETS_BRANCH: "" GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_ENGINE_VERSION: "2.27.1" GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs GH_AW_PROJECT_UTC: "-08:00" GH_AW_PR_HEAD_BASE_BRANCH: "" @@ -532,6 +533,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Set runtime paths @@ -559,6 +561,13 @@ jobs: evidence_tmp="/tmp/gh-aw/agent_execution.json.tmp" printf '{"version":1,"component":"agent","run_id":%s,"run_attempt":%s,"state":"not_started"}\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > "$evidence_tmp" mv "$evidence_tmp" "/tmp/gh-aw/agent_execution.json" + - name: Setup Go + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: '1.26' + cache: false + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - name: Configure gh CLI for GitHub Enterprise @@ -845,6 +854,7 @@ jobs: cat <<'GHAW_MCP_CONFIG_ADAPTER_SCRIPT_7e1a4d2c_EOF' > "${RUNNER_TEMP}/gh-aw/actions/kiro_mcp_config_adapter.cjs" const fs = require("fs"); const path = require("path"); + const log = message => process.stderr.write(`[kiro-mcp] ${message}\n`); const requireEnvVar = name => { const value = process.env[name]; @@ -857,25 +867,47 @@ jobs: const gatewayDomain = process.env.MCP_GATEWAY_DOMAIN || "host.docker.internal"; const gatewayPort = requireEnvVar("MCP_GATEWAY_PORT"); const gatewayURL = `http://${gatewayDomain}:${gatewayPort}`; + log("Reading MCP gateway configuration"); let cliServers; try { - cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]")); - } catch (error) { - throw new Error(`Failed to parse GH_AW_MCP_CLI_SERVERS: ${error instanceof Error ? error.message : String(error)}`); + cliServers = JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"); + } catch { + throw new Error("GH_AW_MCP_CLI_SERVERS must be a JSON array of server names"); + } + if (!Array.isArray(cliServers) || !cliServers.every(name => typeof name === "string")) { + throw new Error("GH_AW_MCP_CLI_SERVERS must be a JSON array of server names"); } + cliServers = new Set(cliServers); - const gatewayOutput = JSON.parse(fs.readFileSync(gatewayOutputPath, "utf8")); - const rawServers = gatewayOutput.mcpServers; - const servers = rawServers && typeof rawServers === "object" && !Array.isArray(rawServers) ? rawServers : {}; + const gatewayContent = fs.readFileSync(gatewayOutputPath, "utf8"); + let gatewayOutput; + try { + gatewayOutput = JSON.parse(gatewayContent); + } catch { + throw new Error("MCP_GATEWAY_OUTPUT must contain valid JSON; check the MCP gateway logs"); + } + const servers = gatewayOutput?.mcpServers; + if (!servers || typeof servers !== "object" || Array.isArray(servers)) { + throw new Error("MCP_GATEWAY_OUTPUT must contain an mcpServers object"); + } const mcpServers = {}; + let skipped = 0; + let httpServers = 0; for (const [name, entry] of Object.entries(servers)) { - if (cliServers.has(name) || !entry || typeof entry !== "object") continue; + if (cliServers.has(name)) { + skipped++; + continue; + } + if (!entry || typeof entry !== "object" || Array.isArray(entry)) { + throw new Error("MCP_GATEWAY_OUTPUT contains an invalid server entry"); + } const transformed = { ...entry }; if (typeof transformed.url === "string") { transformed.url = transformed.url.replace(/^http:\/\/[^/]+\/mcp\//, `${gatewayURL}/mcp/`); transformed.type = "http"; + httpServers++; } delete transformed.tools; mcpServers[name] = transformed; @@ -885,6 +917,7 @@ jobs: fs.mkdirSync(path.dirname(configPath), { recursive: true }); fs.writeFileSync(configPath, JSON.stringify({ mcpServers }, null, 2), { mode: 0o600 }); fs.chmodSync(configPath, 0o600); + log(`Wrote ${Object.keys(mcpServers).length} native MCP server(s), ${httpServers} HTTP server(s), skipped ${skipped} CLI-mounted server(s); permissions=0600`); GHAW_MCP_CONFIG_ADAPTER_SCRIPT_7e1a4d2c_EOF chmod 755 "${RUNNER_TEMP}/gh-aw/actions/kiro_mcp_config_adapter.cjs" @@ -1047,62 +1080,104 @@ jobs: const [, ...commandArgs] = process.argv.slice(2); const installDir = mkdtempSync(join(tmpdir(), "kiro-cli-")); const archive = join(installDir, "kiro-cli.tar.gz"); - const version = "2.16.1"; + const version = "2.27.1"; + const started = Date.now(); const releases = { x64: { arch: "x86_64", - checksum: "393633c991faab5ef688b2aa0dd420481cab7fca312cd863923c3287e9874b82", + checksum: "3c0d7268a4bfb73f8e827822049978fa578e020b271afc1021c7532602d45d99", }, arm64: { arch: "aarch64", - checksum: "8413b62072780747374c0932147022e290725e934d461e430b5ae12a73f33b23", + checksum: "33ad5462c3111ba4ef527f1d58bda08a9d1997e0ae73841a7c2ca734ebcba2d0", }, }; const fail = (result, action) => { if (result.error) throw result.error; - if (result.status !== 0) throw new Error(`${action} failed with exit code ${result.status ?? "unknown"}`); + if (result.status !== 0) { + const detail = result.signal ? `signal ${result.signal}` : `exit code ${result.status ?? "unknown"}`; + const hint = action === "Kiro CLI execution" && result.status === 3 + ? "; required MCP server startup failed; check .kiro/settings/mcp.json and MCP gateway logs" + : ""; + const error = new Error(`${action} failed with ${detail}${hint}`); + error.exitCode = result.status ?? 1; + throw error; + } }; const log = message => process.stderr.write(`[kiro-harness] ${message}\n`); + const run = (command, args, action, options = {}) => { + const start = Date.now(); + log(`${action} started`); + fail(spawnSync(command, args, { stdio: "inherit", ...options }), action); + log(`${action} completed in ${Date.now() - start}ms`); + }; try { + log(`Starting Kiro CLI ${version}; architecture=${process.arch}`); + if (process.env.GH_AW_ENGINE_VERSION && process.env.GH_AW_ENGINE_VERSION !== version) { + throw new Error(`This Kiro harness supports only engine.version ${version}; update the pinned release and checksums together`); + } if (!process.env.KIRO_API_KEY && process.env.SECRET_KIRO_API_KEY) { process.env.KIRO_API_KEY = process.env.SECRET_KIRO_API_KEY; + log("Using KIRO_API_KEY from the secret binding"); } + if (!process.env.KIRO_API_KEY) throw new Error("KIRO_API_KEY is required for headless execution; configure the KIRO_API_KEY Actions secret"); + log("Headless API key is configured"); const release = releases[process.arch]; if (!release) throw new Error(`Unsupported Kiro CLI architecture: ${process.arch}`); - const releaseURL = `https://prod.download.cli.kiro.dev/stable/${version}/kirocli-${release.arch}-linux.tar.gz`; - - fail(spawnSync("curl", ["--fail", "--location", "--silent", "--show-error", "--output", archive, releaseURL], { stdio: "inherit" }), "Kiro CLI download"); - if (createHash("sha256").update(readFileSync(archive)).digest("hex") !== release.checksum) { - throw new Error("Kiro CLI download checksum did not match"); - } - fail(spawnSync("tar", ["-xzf", archive, "-C", installDir], { stdio: "inherit" }), "Kiro CLI extraction"); - - const binDir = join(installDir, "kirocli", "bin"); - const executable = join(binDir, "kiro-cli"); - if (!existsSync(executable)) throw new Error("Kiro CLI executable was not found in the release archive"); - fail(spawnSync(executable, ["--version"], { stdio: "inherit" }), "Kiro CLI verification"); const promptPath = process.env.GH_AW_PROMPT; if (!promptPath) throw new Error("GH_AW_PROMPT is required"); const prompt = readFileSync(promptPath, "utf8"); + if (!prompt.trim()) throw new Error("GH_AW_PROMPT must contain a non-empty instruction"); const selectedModel = process.env.KIRO_MODEL; if (!selectedModel?.startsWith("kiro/")) { throw new Error("KIRO_MODEL must use kiro/model format"); } const model = selectedModel.slice("kiro/".length); - if (!model) throw new Error("KIRO_MODEL must include a model name"); + if (!model.trim()) throw new Error("KIRO_MODEL must include a model name"); + log(`Prompt loaded (${Buffer.byteLength(prompt, "utf8")} bytes); model override is configured`); - fail(spawnSync(executable, [...commandArgs, "--model", model, prompt], { + const mcpConfigPath = process.env.GH_AW_MCP_CONFIG; + if (mcpConfigPath) { + let config; + const content = readFileSync(mcpConfigPath, "utf8"); + try { + config = JSON.parse(content); + } catch { + throw new Error("GH_AW_MCP_CONFIG must contain valid JSON; check the Kiro MCP adapter logs"); + } + if (!config?.mcpServers || typeof config.mcpServers !== "object" || Array.isArray(config.mcpServers)) { + throw new Error("GH_AW_MCP_CONFIG must contain an mcpServers object"); + } + log(`Native MCP configuration loaded (${Object.keys(config.mcpServers).length} server(s))`); + } else { + log("No GH_AW_MCP_CONFIG path supplied; Kiro will use its default MCP configuration"); + } + + const releaseURL = `https://prod.download.cli.kiro.dev/stable/${version}/kirocli-${release.arch}-linux.tar.gz`; + run("curl", ["--fail", "--location", "--silent", "--show-error", "--output", archive, releaseURL], "Kiro CLI download"); + if (createHash("sha256").update(readFileSync(archive)).digest("hex") !== release.checksum) { + throw new Error("Kiro CLI download checksum did not match"); + } + log("Kiro CLI SHA-256 checksum verified"); + run("tar", ["-xzf", archive, "-C", installDir], "Kiro CLI extraction"); + + const binDir = join(installDir, "kirocli", "bin"); + const executable = join(binDir, "kiro-cli"); + if (!existsSync(executable)) throw new Error("Kiro CLI executable was not found in the release archive"); + run(executable, ["--version"], "Kiro CLI verification"); + + run(executable, [...commandArgs, "--model", model, "--", prompt], "Kiro CLI execution", { cwd: process.env.GITHUB_WORKSPACE, env: { ...process.env, PATH: `${binDir}:${process.env.PATH || ""}` }, - stdio: "inherit", - }), "Kiro CLI execution"); + }); } catch (error) { log(error instanceof Error ? error.message : String(error)); - process.exitCode = 1; + process.exitCode = Number.isInteger(error?.exitCode) && error.exitCode > 0 ? error.exitCode : 1; } finally { rmSync(installDir, { recursive: true, force: true }); + log(`Cleaned up Kiro CLI installation; total duration=${Date.now() - started}ms; exit code=${process.exitCode || 0}`); } GHAW_HARNESS_SCRIPT_3c7b9f1a_EOF @@ -1125,7 +1200,7 @@ jobs: if [[ ! "$GH_AW_MAX_AI_CREDITS" =~ ^[0-9]+$ ]]; then GH_AW_MAX_AI_CREDITS="1000" fi - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.31/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.grafana.net\",\"*.kiro.dev\",\"*.sentry.io\",\"api.github.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"client-telemetry.us-east-1.amazonaws.com\",\"codeload.github.com\",\"codewhisperer.us-east-1.amazonaws.com\",\"cognito-identity.us-east-1.amazonaws.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"docs.github.com\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"patchdiff.githubusercontent.com\",\"ppa.launchpad.net\",\"prod.assets.shortbread.aws.dev\",\"prod.us-east-1.telemetry.kiro.aws.dev\",\"q.us-east-1.amazonaws.com\",\"raw.githubusercontent.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-6\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-3.8-flash\":[\"copilot/gemini-3.8*flash*\",\"google/gemini-3.8*flash*\",\"gemini/gemini-3.8*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"gpt-6\":[\"copilot/gpt-6*\",\"openai/gpt-6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-6\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.31,squid=sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d,agent=sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76,api-proxy=sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a,cli-proxy=sha256:505df66052e6688cf419ad248a511d17093475bd66a958618078d3c9cdf95083\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.31/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.grafana.net\",\"*.kiro.dev\",\"*.sentry.io\",\"api.github.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"client-telemetry.us-east-1.amazonaws.com\",\"codeload.github.com\",\"codewhisperer.us-east-1.amazonaws.com\",\"cognito-identity.us-east-1.amazonaws.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"docs.github.com\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"go.dev\",\"golang.org\",\"goproxy.io\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"patchdiff.githubusercontent.com\",\"pkg.go.dev\",\"ppa.launchpad.net\",\"prod.assets.shortbread.aws.dev\",\"prod.us-east-1.telemetry.kiro.aws.dev\",\"proxy.golang.org\",\"q.us-east-1.amazonaws.com\",\"raw.githubusercontent.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"storage.googleapis.com\",\"sum.golang.org\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-6\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-3.8-flash\":[\"copilot/gemini-3.8*flash*\",\"google/gemini-3.8*flash*\",\"gemini/gemini-3.8*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"gpt-6\":[\"copilot/gpt-6*\",\"openai/gpt-6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-6\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.31,squid=sha256:90a46d2e6e910ace2c09c2dcf6b56dc374ed513d9da642a4e9fcfcd6e9248a9d,agent=sha256:c4ab1d48d533cc7daaa5f2e1193d1a644888242fb8813d49c6de46d129224b76,api-proxy=sha256:a5a37489635109334a5e2b5cb2eaba32e8c8f1a89d8dea6009baa962a8904c4a,cli-proxy=sha256:505df66052e6688cf419ad248a511d17093475bd66a958618078d3c9cdf95083\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -1147,6 +1222,7 @@ jobs: -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export no_proxy="${NO_PROXY:-}" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; GH_AW_TOOL_BINS="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')"; GH_AW_TOOL_BINS="${GH_AW_TOOL_BINS%:}"; export PATH="$PATH${GH_AW_TOOL_BINS:+:}$GH_AW_TOOL_BINS"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/kiro_harness.cjs kiro-cli chat --no-interactive --trust-all-tools --require-mcp-startup' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: AWF_REFLECT_ENABLED: 1 + GH_AW_ENGINE_VERSION: 2.27.1 GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} GH_AW_MCP_CONFIG: .kiro/settings/mcp.json @@ -1208,7 +1284,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_COMMANDS: "[\"smoke-kiro\"]" @@ -1447,6 +1523,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Download agent output artifact @@ -1768,6 +1845,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Download activation artifact @@ -2095,6 +2173,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Check command position @@ -2147,6 +2226,7 @@ jobs: GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} GH_AW_ENGINE_ID: "kiro" GH_AW_ENGINE_MODEL: "kiro/auto" + GH_AW_ENGINE_VERSION: "2.27.1" GH_AW_HEAD_SHA: ${{ github.event.pull_request.head.sha }} GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} @@ -2195,6 +2275,7 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Kiro" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-kiro.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.27.1" GH_AW_INFO_ENGINE_ID: "kiro" GH_AW_SETUP_AW_CONTEXT: ${{ inputs.aw_context }} - name: Mask OTLP telemetry headers @@ -2231,7 +2312,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"add_labels\":{\"allowed\":[\"smoke-kiro\"]},\"create_issue\":{\"close_older_issues\":true,\"close_older_key\":\"smoke-kiro\",\"expires\":2,\"labels\":[\"automation\",\"testing\"],\"max\":1},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":2,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" diff --git a/.github/workflows/smoke-kiro.md b/.github/workflows/smoke-kiro.md index 09eda076779..a0bfea05cd4 100644 --- a/.github/workflows/smoke-kiro.md +++ b/.github/workflows/smoke-kiro.md @@ -25,7 +25,6 @@ engine: strict: true imports: - shared/kiro.md - - shared/gh.md - shared/reporting-otlp.md - shared/otlp.md - shared/token-telemetry-check.md @@ -34,6 +33,10 @@ network: allowed: - defaults - github + - go +runtimes: + go: + version: "1.26" safe-outputs: allowed-domains: [default-safe-outputs] add-comment: @@ -63,10 +66,11 @@ sandbox: ## Test Requirements -1. **GitHub MCP Testing**: Use GitHub MCP tools to fetch details of exactly 2 merged pull requests from ${{ github.repository }} (title and number only) +1. **GitHub MCP Testing**: Use native GitHub MCP tools to fetch details of exactly 2 merged pull requests from ${{ github.repository }} (title and number only). Do not substitute `gh`, curl, or another CLI for this test. If native GitHub MCP tools are unavailable or fail, mark this test as FAIL. 2. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-kiro-${{ github.run_id }}.txt` with content "Smoke test passed for Kiro at $(date)" (create the directory if it doesn't exist) 3. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back) 4. **Build gh-aw**: Run `GOCACHE=/tmp/gh-aw/agent/go-cache GOMODCACHE=/tmp/gh-aw/agent/go-mod make build` to verify the agent can successfully build the gh-aw project. If the command fails, mark this test as failed and report the failure. +5. **Runtime Configuration Testing**: Verify `kiro-cli --version` matches `GH_AW_ENGINE_VERSION`. Use Node.js to inspect `GH_AW_MCP_CONFIG` and verify it contains a native GitHub MCP server with `type: "http"`, a container-reachable URL, and an authorization header. Report only PASS or FAIL; never print configuration contents, headers, keys, or the prompt. ## Output diff --git a/pkg/workflow/kiro_workflow_test.go b/pkg/workflow/kiro_workflow_test.go index 750649257e2..8332f120e86 100644 --- a/pkg/workflow/kiro_workflow_test.go +++ b/pkg/workflow/kiro_workflow_test.go @@ -3,11 +3,38 @@ package workflow import ( + "encoding/json" "os" + "os/exec" + "path/filepath" "strings" "testing" + + "gopkg.in/yaml.v3" ) +func loadKiroWorkflowDefinition(t *testing.T) EngineDefinition { + t.Helper() + content, err := os.ReadFile("../../.github/workflows/shared/kiro.md") + if err != nil { + t.Fatal(err) + } + parts := strings.SplitN(string(content), "---", 3) + if len(parts) != 3 { + t.Fatal("shared Kiro workflow has invalid frontmatter") + } + var frontmatter struct { + Engine EngineDefinition `yaml:"engine"` + } + if err := yaml.Unmarshal([]byte(parts[1]), &frontmatter); err != nil { + t.Fatal(err) + } + if frontmatter.Engine.Behaviors == nil || frontmatter.Engine.Behaviors.MCP == nil { + t.Fatal("shared Kiro workflow has no MCP behaviors") + } + return frontmatter.Engine +} + func TestKiroWorkflowConfiguresContainerRuntime(t *testing.T) { for _, path := range []string{ "../../.github/workflows/shared/kiro.md", @@ -30,3 +57,314 @@ func TestKiroWorkflowConfiguresContainerRuntime(t *testing.T) { } } } + +func TestKiroSmokeExercisesNativeMCPAndGoBuild(t *testing.T) { + content, err := os.ReadFile("../../.github/workflows/smoke-kiro.md") + if err != nil { + t.Fatal(err) + } + source := string(content) + for _, expected := range []string{ + " - go\n", + "runtimes:\n go:\n", + "Do not substitute `gh`, curl, or another CLI", + "`kiro-cli --version` matches `GH_AW_ENGINE_VERSION`", + } { + if !strings.Contains(source, expected) { + t.Errorf("smoke-kiro must contain %q", expected) + } + } + if strings.Contains(source, "shared/gh.md") { + t.Error("smoke-kiro must not replace native GitHub MCP with gh-proxy") + } + + lock, err := os.ReadFile("../../.github/workflows/smoke-kiro.lock.yml") + if err != nil { + t.Fatal(err) + } + for _, expected := range []string{ + "GH_AW_ENGINE_VERSION: " + loadKiroWorkflowDefinition(t).Version, + "proxy.golang.org", + "sum.golang.org", + "storage.googleapis.com", + `"container": "ghcr.io/github/github-mcp-server:`, + `"GITHUB_READ_ONLY": "1"`, + } { + if !strings.Contains(string(lock), expected) { + t.Errorf("compiled smoke-kiro must contain %q", expected) + } + } + if strings.Contains(string(lock), "--enable-gh-proxy") { + t.Error("compiled smoke-kiro must not replace native GitHub MCP with gh-proxy") + } +} + +func TestKiroMCPAdapter(t *testing.T) { + adapter := loadKiroWorkflowDefinition(t).Behaviors.MCP.ConfigAdapter + t.Run("native config and secret-safe logging", func(t *testing.T) { + workspace := t.TempDir() + gateway := `{"mcpServers":{ + "github":{"url":"http://localhost:8080/mcp/github","headers":{"Authorization":"Bearer fixture-mcp-secret"},"tools":["search"]}, + "safeoutputs":{"url":"http://localhost:8080/mcp/safeoutputs"}, + "local":{"command":"node","args":["server.cjs"]} + }}` + output, err := runKiroAdapter(t, adapter, workspace, gateway, `["safeoutputs"]`) + if err != nil { + t.Fatalf("adapter failed: %v\n%s", err, output) + } + assertKiroMCPConfig(t, workspace) + if !strings.Contains(output, "2 native MCP server(s), 1 HTTP server(s), skipped 1 CLI-mounted server(s); permissions=0600") { + t.Fatalf("missing adapter diagnostics:\n%s", output) + } + if strings.Contains(output, "fixture-mcp-secret") || strings.Contains(output, "Authorization") { + t.Fatalf("adapter logs exposed credentials:\n%s", output) + } + }) + for _, test := range []struct { + name, gateway, cliServers, want string + }{ + {"malformed gateway", `{"secret":"fixture-mcp-secret"`, `[]`, "must contain valid JSON"}, + {"missing servers", `{}`, `[]`, "must contain an mcpServers object"}, + {"array servers", `{"mcpServers":[]}`, `[]`, "must contain an mcpServers object"}, + {"invalid entry", `{"mcpServers":{"github":null}}`, `[]`, "invalid server entry"}, + {"malformed CLI list", `{"mcpServers":{}}`, `fixture-mcp-secret`, "must be a JSON array"}, + {"object CLI list", `{"mcpServers":{}}`, `{}`, "must be a JSON array"}, + {"non-string CLI name", `{"mcpServers":{}}`, `[1]`, "must be a JSON array"}, + } { + t.Run(test.name, func(t *testing.T) { + output, err := runKiroAdapter(t, adapter, t.TempDir(), test.gateway, test.cliServers) + if err == nil || !strings.Contains(output, test.want) || strings.Contains(output, "fixture-mcp-secret") { + t.Fatalf("expected secret-safe failure %q, got %v:\n%s", test.want, err, output) + } + }) + } +} + +func runKiroAdapter(t *testing.T, adapter, workspace, gateway, cliServers string) (string, error) { + t.Helper() + scriptPath := filepath.Join(workspace, "adapter.cjs") + gatewayPath := filepath.Join(workspace, "gateway.json") + for path, content := range map[string]string{scriptPath: adapter, gatewayPath: gateway} { + if err := os.WriteFile(path, []byte(content), 0o600); err != nil { + t.Fatal(err) + } + } + cmd := exec.Command("node", scriptPath) + cmd.Env = []string{ + "PATH=" + os.Getenv("PATH"), + "GITHUB_WORKSPACE=" + workspace, + "MCP_GATEWAY_OUTPUT=" + gatewayPath, + "MCP_GATEWAY_PORT=8080", + "GH_AW_MCP_CLI_SERVERS=" + cliServers, + } + output, err := cmd.CombinedOutput() + return string(output), err +} + +func assertKiroMCPConfig(t *testing.T, workspace string) { + t.Helper() + configPath := filepath.Join(workspace, ".kiro", "settings", "mcp.json") + content, err := os.ReadFile(configPath) + if err != nil { + t.Fatal(err) + } + var config struct { + MCPServers map[string]struct { + URL string `json:"url"` + Type string `json:"type"` + Headers map[string]string `json:"headers"` + Tools []string `json:"tools"` + Command string `json:"command"` + } `json:"mcpServers"` + } + if err := json.Unmarshal(content, &config); err != nil { + t.Fatal(err) + } + github := config.MCPServers["github"] + if len(config.MCPServers) != 2 || github.URL != "http://host.docker.internal:8080/mcp/github" || + github.Type != "http" || github.Headers["Authorization"] != "Bearer fixture-mcp-secret" || len(github.Tools) != 0 { + t.Error("adapter did not preserve native HTTP MCP configuration") + } + if config.MCPServers["local"].Command != "node" { + t.Error("adapter did not preserve the stdio server") + } + info, err := os.Stat(configPath) + if err != nil { + t.Fatal(err) + } + if info.Mode().Perm() != 0o600 { + t.Errorf("MCP config permissions = %o, want 600", info.Mode().Perm()) + } +} + +func TestKiroHarness(t *testing.T) { + definition := loadKiroWorkflowDefinition(t) + if definition.Version != "2.27.1" { + t.Fatalf("expected verified Kiro release 2.27.1, got %q", definition.Version) + } + scriptPath := filepath.Join(t.TempDir(), "kiro_harness.cjs") + if err := os.WriteFile(scriptPath, []byte(definition.Behaviors.HarnessScript), 0o600); err != nil { + t.Fatal(err) + } + for _, scenario := range []string{ + "success", "arm64", "secret-fallback", "no-mcp", "missing-key", + "missing-prompt", "empty-prompt", "invalid-model", "empty-model", + "version-mismatch", "unsupported-arch", "invalid-mcp", "missing-mcp-servers", + "checksum", "download", "missing-binary", "verification", "exit3", "exit7", "signal", "spawn", + } { + t.Run(scenario, func(t *testing.T) { + cmd := exec.Command("node", "-e", kiroHarnessTestDriver, scriptPath, scenario) + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Kiro harness scenario failed: %v\n%s", err, output) + } + }) + } +} + +const kiroHarnessTestDriver = ` + const assert = require("node:assert/strict"); + const fs = require("node:fs"); + const vm = require("node:vm"); + const [scriptPath, scenario] = process.argv.slice(1); + const calls = []; + const logs = []; + let cleaned = false; + const prompt = "--fixture-private-prompt"; + const apiKey = "fixture-api-key"; + const mcpSecret = "fixture-mcp-secret"; + const arch = scenario === "arm64" ? "arm64" : scenario === "unsupported-arch" ? "riscv64" : "x64"; + const checksum = arch === "arm64" + ? "33ad5462c3111ba4ef527f1d58bda08a9d1997e0ae73841a7c2ca734ebcba2d0" + : "3c0d7268a4bfb73f8e827822049978fa578e020b271afc1021c7532602d45d99"; + const env = { + KIRO_API_KEY: apiKey, KIRO_MODEL: "kiro/auto", GH_AW_ENGINE_VERSION: "2.27.1", + GH_AW_PROMPT: "/prompt.md", GITHUB_WORKSPACE: "/workspace", + GH_AW_MCP_CONFIG: "/workspace/.kiro/settings/mcp.json", PATH: "/usr/bin", + }; + if (scenario === "secret-fallback") { + delete env.KIRO_API_KEY; + env.SECRET_KIRO_API_KEY = apiKey; + } + if (scenario === "missing-key") delete env.KIRO_API_KEY; + if (scenario === "missing-prompt") delete env.GH_AW_PROMPT; + if (scenario === "invalid-model") env.KIRO_MODEL = "openai/auto"; + if (scenario === "empty-model") env.KIRO_MODEL = "kiro/ "; + if (scenario === "version-mismatch") env.GH_AW_ENGINE_VERSION = "2.16.1"; + if (scenario === "no-mcp") delete env.GH_AW_MCP_CONFIG; + const mockFS = { + mkdtempSync: () => "/tmp/kiro-fixture", + existsSync: () => scenario !== "missing-binary", + readFileSync: path => { + if (path === "/prompt.md") return scenario === "empty-prompt" ? " \n" : prompt; + if (path.endsWith("mcp.json")) { + if (scenario === "invalid-mcp") return '{"secret":"' + mcpSecret; + if (scenario === "missing-mcp-servers") return "{}"; + return JSON.stringify({mcpServers: {github: { + url: "http://host.docker.internal:8080/mcp/github", + headers: {Authorization: "Bearer " + mcpSecret}, + }}}); + } + assert.equal(path, "/tmp/kiro-fixture/kiro-cli.tar.gz"); + return Buffer.from("fixture-archive"); + }, + rmSync: (path, options) => { + assert.equal(path, "/tmp/kiro-fixture"); + assert.equal(options.recursive, true); + cleaned = true; + }, + }; + const spawnSync = (command, args, options) => { + calls.push({command, args, options}); + assert.equal(options.shell, undefined); + assert.equal(options.stdio, "inherit"); + if (scenario === "download" && command === "curl") return {status: 22}; + if (scenario === "verification" && args[0] === "--version") return {status: 9}; + if (args[0] === "chat") { + if (scenario === "exit3") return {status: 3}; + if (scenario === "exit7") return {status: 7}; + if (scenario === "signal") return {status: null, signal: "SIGTERM"}; + if (scenario === "spawn") return {error: new Error("spawn ENOENT"), status: null}; + } + return {status: 0}; + }; + const mockProcess = { + argv: ["node", scriptPath, "kiro-cli", "chat", "--no-interactive", "--trust-all-tools", "--require-mcp-startup"], + env, arch, stderr: {write: text => logs.push(text)}, + }; + vm.runInNewContext(fs.readFileSync(scriptPath, "utf8"), { + require: name => { + if (name === "fs") return mockFS; + if (name === "child_process") return {spawnSync}; + if (name === "crypto") return {createHash: algorithm => { + assert.equal(algorithm, "sha256"); + return {update: data => { + assert.equal(data.toString(), "fixture-archive"); + return {digest: encoding => { + assert.equal(encoding, "hex"); + return scenario === "checksum" ? "bad-checksum" : checksum; + }}; + }}; + }}; + return require(name); + }, + process: mockProcess, Buffer, + }); + assert.equal(cleaned, true); + const output = logs.join(""); + for (const secret of [prompt, apiKey, mcpSecret, "Authorization"]) { + assert.equal(output.includes(secret), false, "logs exposed " + secret); + } + assert.ok(output.includes("[kiro-harness] Starting Kiro CLI 2.27.1")); + assert.ok(output.includes("Cleaned up Kiro CLI installation; total duration=")); + const failures = { + "missing-key": [1, "KIRO_API_KEY is required"], + "missing-prompt": [1, "GH_AW_PROMPT is required"], + "empty-prompt": [1, "non-empty instruction"], + "invalid-model": [1, "kiro/model format"], + "empty-model": [1, "must include a model name"], + "version-mismatch": [1, "supports only engine.version 2.27.1"], + "unsupported-arch": [1, "Unsupported Kiro CLI architecture"], + "invalid-mcp": [1, "GH_AW_MCP_CONFIG must contain valid JSON"], + "missing-mcp-servers": [1, "must contain an mcpServers object"], + "checksum": [1, "checksum did not match"], + "download": [22, "download failed with exit code 22"], + "missing-binary": [1, "executable was not found"], + "verification": [9, "verification failed with exit code 9"], + "exit3": [3, "required MCP server startup failed"], + "exit7": [7, "execution failed with exit code 7"], + "signal": [1, "execution failed with signal SIGTERM"], + "spawn": [1, "spawn ENOENT"], + }; + if (failures[scenario]) { + const [code, detail] = failures[scenario]; + assert.equal(mockProcess.exitCode, code); + assert.ok(output.includes(detail), output); + assert.equal(output.includes("Kiro CLI execution completed"), false); + if (["missing-key", "missing-prompt", "empty-prompt", "invalid-model", "empty-model", + "version-mismatch", "unsupported-arch", "invalid-mcp", "missing-mcp-servers"].includes(scenario)) { + assert.equal(calls.length, 0, "invalid input must fail before downloading"); + } + } else { + assert.equal(mockProcess.exitCode || 0, 0); + assert.equal(calls.length, 4); + assert.equal(calls[0].command, "curl"); + assert.ok(calls[0].args.at(-1).endsWith( + "/2.27.1/kirocli-" + (arch === "arm64" ? "aarch64" : "x86_64") + "-linux.tar.gz" + )); + assert.deepEqual(Array.from(calls[1].args), ["-xzf", "/tmp/kiro-fixture/kiro-cli.tar.gz", "-C", "/tmp/kiro-fixture"]); + assert.deepEqual(Array.from(calls[2].args), ["--version"]); + assert.deepEqual(Array.from(calls[3].args), [ + "chat", "--no-interactive", "--trust-all-tools", "--require-mcp-startup", + "--model", "auto", "--", prompt, + ]); + assert.equal(calls[3].options.cwd, "/workspace"); + assert.equal(calls[3].options.env.KIRO_API_KEY, apiKey); + assert.equal(calls[3].options.env.PATH, "/tmp/kiro-fixture/kirocli/bin:/usr/bin"); + assert.ok(output.includes("Kiro CLI SHA-256 checksum verified")); + assert.ok(output.includes("Kiro CLI execution completed in ")); + assert.ok(output.includes("Prompt loaded (")); + assert.ok(output.includes(scenario === "no-mcp" + ? "No GH_AW_MCP_CONFIG path supplied" : "Native MCP configuration loaded (1 server(s))")); + } + ` diff --git a/pkg/workflow/schemas/github-workflow.json b/pkg/workflow/schemas/github-workflow.json index d3266559cb7..61ac6c46a6a 100644 --- a/pkg/workflow/schemas/github-workflow.json +++ b/pkg/workflow/schemas/github-workflow.json @@ -42,15 +42,8 @@ "queue": { "$comment": "https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#example-queueing-multiple-pending-runs", "description": "Controls how pending jobs or workflow runs are queued within a concurrency group. With the default `single`, at most one run can be pending — additional pending runs cancel the previous one. With `max`, up to 100 runs can be pending and are processed in FIFO order. The combination of `queue: max` and `cancel-in-progress: true` is not allowed.", - "oneOf": [ - { - "type": "string", - "enum": ["single", "max"] - }, - { - "$ref": "#/definitions/expressionSyntax" - } - ], + "type": "string", + "enum": ["single", "max"], "default": "single" } },