diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..c1965c2
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+.github/workflows/*.lock.yml linguist-generated=true merge=ours
\ No newline at end of file
diff --git a/.github/mcp.json b/.github/mcp.json
new file mode 100644
index 0000000..b953af2
--- /dev/null
+++ b/.github/mcp.json
@@ -0,0 +1,11 @@
+{
+ "mcpServers": {
+ "github-agentic-workflows": {
+ "command": "gh",
+ "args": [
+ "aw",
+ "mcp-server"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..9c6b2d9
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,51 @@
+name: CI
+on:
+ push:
+ pull_request:
+permissions:
+ contents: read
+jobs:
+ build-test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Typecheck
+ run: npm run typecheck
+
+ - name: Test
+ run: npm test
+
+ integration-test:
+ runs-on: ubuntu-latest
+ needs: build-test
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Integration test
+ env:
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ run: |
+ if [ -z "${COPILOT_GITHUB_TOKEN:-}" ]; then
+ echo "Skipping integration tests: COPILOT_GITHUB_TOKEN is not available."
+ else
+ npm run test:integration
+ fi
diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml
new file mode 100644
index 0000000..433acf7
--- /dev/null
+++ b/.github/workflows/copilot-setup-steps.yml
@@ -0,0 +1,26 @@
+name: "Copilot Setup Steps"
+
+# This workflow configures the environment for GitHub Copilot Agent with gh-aw MCP server
+on:
+ workflow_dispatch:
+ push:
+ paths:
+ - .github/workflows/copilot-setup-steps.yml
+
+jobs:
+ # The job MUST be called 'copilot-setup-steps' to be recognized by GitHub Copilot Agent
+ copilot-setup-steps:
+ runs-on: ubuntu-latest
+
+ # Set minimal permissions for setup steps
+ # Copilot Agent receives its own token with appropriate permissions
+ permissions:
+ contents: read
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ - name: Install gh-aw extension
+ uses: github/gh-aw-actions/setup-cli@v0.77.5
+ with:
+ version: v0.77.5
diff --git a/.github/workflows/daily-rig-sampler.lock.yml b/.github/workflows/daily-rig-sampler.lock.yml
new file mode 100644
index 0000000..0818d3e
--- /dev/null
+++ b/.github/workflows/daily-rig-sampler.lock.yml
@@ -0,0 +1,1532 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"beabd252ffc57667a5e80e7290e9d09ee0849ba36d0e6ba341028bf9c75b466a","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot"}
+# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"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":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.77.5","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.19"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.77.5). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Each day, pick one rig sample (cached round-robin) from src/samples/, run it with the rig skill, analyze harness performance, and apply one focused quick-win improvement to skills/rig/rig.ts in a new draft PR.
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_CI_TRIGGER_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - 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-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@v0.77.5
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.55
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55
+# - ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.55
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.55
+# - ghcr.io/github/gh-aw-mcpg:v0.3.19
+# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4
+# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+
+name: "Daily Rig Sampler"
+on:
+ schedule:
+ - cron: "37 16 * * *"
+ # Friendly format: daily (scattered)
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Daily Rig Sampler"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-rig-sampler.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AGENT_VERSION: "1.0.52"
+ GH_AW_INFO_CLI_VERSION: "v0.77.5"
+ GH_AW_INFO_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","node"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ env:
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ - name: Checkout .github and .agents folders
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .antigravity
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "daily-rig-sampler.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.77.5"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_a110ec9a16ecfb3b_EOF'
+
+ GH_AW_PROMPT_a110ec9a16ecfb3b_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
+ cat << 'GH_AW_PROMPT_a110ec9a16ecfb3b_EOF'
+
+ Tools: create_pull_request, missing_tool, missing_data, noop
+ GH_AW_PROMPT_a110ec9a16ecfb3b_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat << 'GH_AW_PROMPT_a110ec9a16ecfb3b_EOF'
+
+ GH_AW_PROMPT_a110ec9a16ecfb3b_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_a110ec9a16ecfb3b_EOF'
+
+ The following GitHub context information is available for this workflow:
+ {{#if github.actor}}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if github.repository}}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if github.workspace}}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
+ - **issue-number**: #__GH_AW_EXPR_802A9F6A__
+ {{/if}}
+ {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
+ - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
+ {{/if}}
+ {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
+ - **pull-request-number**: #__GH_AW_EXPR_463A214A__
+ {{/if}}
+ {{#if github.event.comment.id || github.aw.context.comment_id}}
+ - **comment-id**: __GH_AW_EXPR_FF1D34CE__
+ {{/if}}
+ {{#if github.run_id}}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_a110ec9a16ecfb3b_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_a110ec9a16ecfb3b_EOF'
+
+ {{#runtime-import .github/workflows/daily-rig-sampler.md}}
+ GH_AW_PROMPT_a110ec9a16ecfb3b_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ALLOWED_EXTENSIONS: ''
+ GH_AW_CACHE_DESCRIPTION: ''
+ GH_AW_CACHE_DIR: '/tmp/gh-aw/cache-memory/'
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_ALLOWED_EXTENSIONS: process.env.GH_AW_ALLOWED_EXTENSIONS,
+ GH_AW_CACHE_DESCRIPTION: process.env.GH_AW_CACHE_DESCRIPTION,
+ GH_AW_CACHE_DIR: process.env.GH_AW_CACHE_DIR,
+ GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
+ GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
+ GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
+ GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
+ GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ /tmp/gh-aw/.github/skills
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ issues: read
+ pull-requests: read
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: dailyrigsampler
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-rig-sampler.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - 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
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
+ env:
+ GH_TOKEN: ${{ github.token }}
+ # Cache memory file share configuration from frontmatter processed below
+ - name: Create cache-memory directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
+ - name: Restore cache-memory file share data
+ uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
+ path: /tmp/gh-aw/cache-memory
+ restore-keys: |
+ memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-
+ - name: Setup cache-memory git repository
+ env:
+ GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
+ GH_AW_MIN_INTEGRITY: none
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/setup_cache_memory_git.sh"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Checkout PR branch
+ id: checkout-pr
+ if: |
+ github.event.pull_request || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Restore inline skills from activation artifact
+ env:
+ GH_AW_SKILL_DIR: ".github/skills"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/squid:0.25.55 ghcr.io/github/gh-aw-mcpg:v0.3.19 ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+ - name: Generate Safe Outputs Config
+ run: |
+ mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
+ mkdir -p /tmp/gh-aw/safeoutputs
+ mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a411d4098841fcb1_EOF'
+ {"create_pull_request":{"allowed_files":["skills/rig/rig.ts"],"draft":true,"labels":["automation","ai-agent"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"request_review","reviewers":["copilot"],"title_prefix":"[rig-sampler] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_a411d4098841fcb1_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[rig-sampler] \". Labels [\"automation\" \"ai-agent\"] will be automatically added. PRs will be created as drafts. Reviewers [\"copilot\"] will be assigned."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_pull_request": {
+ "defaultMax": 1,
+ "fields": {
+ "base": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "branch": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "draft": {
+ "type": "boolean"
+ },
+ "labels": {
+ "type": "array",
+ "itemType": "string",
+ "itemSanitize": true,
+ "itemMaxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ run: |
+ set -eo pipefail
+ mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ case "${DOCKER_HOST:-}" in
+ unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
+ /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
+ * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
+ esac
+ DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.19'
+
+ mkdir -p /home/runner/.copilot
+ GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
+ cat << GH_AW_MCP_CONFIG_5b483aba2b2317da_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_5b483aba2b2317da_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Start CLI Proxy
+ env:
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ CLI_PROXY_POLICY: '{"allow-only":{"repos":"all","min-integrity":"none"}}'
+ CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.3.19'
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 30
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.npms.io","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","bun.sh","cdn.jsdelivr.net","codeload.github.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","deb.nodesource.com","deno.land","docs.github.com","esm.sh","get.pnpm.io","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","googleapis.deno.dev","googlechromelabs.github.io","host.docker.internal","json-schema.org","json.schemastore.org","jsr.io","keyserver.ubuntu.com","lfs.github.com","nodejs.org","npm.pkg.github.com","npmjs.com","npmjs.org","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","ppa.launchpad.net","raw.githubusercontent.com","registry.bower.io","registry.npmjs.com","registry.npmjs.org","registry.yarnpkg.com","repo.yarnpkg.com","s.symcb.com","s.symcd.com","security.ubuntu.com","skimdb.npmjs.com","storage.googleapis.com","telemetry.enterprise.githubcopilot.com","telemetry.vercel.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com","www.npmjs.com","www.npmjs.org","yarnpkg.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"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*"],"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*","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-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-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"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.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*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/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_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.77.5
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Stop CLI Proxy
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/stop_cli_proxy.sh"
+ - name: Detect agent errors
+ if: always()
+ id: detect-agent-errors
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - name: Stop MCP Gateway
+ if: always()
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
+ await main();
+ env:
+ GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
+ SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ 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,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,codeload.github.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,deb.nodesource.com,deno.land,docs.github.com,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ await main();
+ - name: Print firewall logs
+ if: always()
+ continue-on-error: true
+ env:
+ AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
+ run: |
+ # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Commit cache-memory changes
+ if: always()
+ env:
+ GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/commit_cache_memory_git.sh"
+ - name: Upload cache-memory data as artifact
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ if: always()
+ with:
+ name: cache-memory
+ include-hidden-files: true
+ path: /tmp/gh-aw/cache-memory
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ - update_cache_memory
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+ concurrency:
+ group: "gh-aw-conclusion-daily-rig-sampler"
+ cancel-in-progress: false
+ queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-rig-sampler.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/daily-rig-sampler.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/daily-rig-sampler.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/daily-rig-sampler.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/daily-rig-sampler.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/daily-rig-sampler.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "daily-rig-sampler"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
+ GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }}
+ GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "30"
+ GH_AW_MAX_EFFECTIVE_TOKENS: "25000000"
+ GH_AW_CACHE_MEMORY_ENABLED: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-rig-sampler.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/squid:0.25.55
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "Daily Rig Sampler"
+ WORKFLOW_DESCRIPTION: "Each day, pick one rig sample (cached round-robin) from src/samples/, run it with the rig skill, analyze harness performance, and apply one focused quick-win improvement to skills/rig/rig.ts in a new draft PR."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/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_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.77.5
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError && !detectionExecutionFailed) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-rig-sampler"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.52"
+ GH_AW_WORKFLOW_ID: "daily-rig-sampler"
+ GH_AW_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/daily-rig-sampler.md"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }}
+ created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-rig-sampler.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Download patch artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Extract base branch from agent output
+ id: extract-base-branch
+ if: steps.download-agent-output.outcome == 'success'
+ shell: bash
+ run: |
+ if [ -f "/tmp/gh-aw/agent_output.json" ]; then
+ GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
+ BASE_BRANCH=$("$GH_AW_NODE" -e "
+ try {
+ const data = JSON.parse(require('fs').readFileSync('/tmp/gh-aw/agent_output.json', 'utf8'));
+ const item = (data.items || []).find(i =>
+ (i.type === 'create_pull_request' || i.type === 'push_to_pull_request_branch') &&
+ i.base_branch
+ );
+ if (item) process.stdout.write(item.base_branch);
+ } catch(e) {}
+ " 2>/dev/null || true)
+ # Validate: only allow safe git branch name characters
+ if [[ "$BASE_BRANCH" =~ ^[a-zA-Z0-9/_.-]+$ ]] && [ ${#BASE_BRANCH} -le 255 ]; then
+ printf 'base-branch=%s\n' "$BASE_BRANCH" >> "$GITHUB_OUTPUT"
+ echo "Extracted base branch from safe output: $BASE_BRANCH"
+ fi
+ fi
+ - name: Checkout repository (trusted default branch for comment events)
+ if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ github.event.repository.default_branch }}
+ token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ persist-credentials: false
+ fetch-depth: 1
+ - name: Checkout repository
+ if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
+ token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ persist-credentials: false
+ fetch-depth: 1
+ - name: Configure Git credentials
+ if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,codeload.github.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,deb.nodesource.com,deno.land,docs.github.com,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"allowed_files\":[\"skills/rig/rig.ts\"],\"draft\":true,\"labels\":[\"automation\",\"ai-agent\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"request_review\",\"reviewers\":[\"copilot\"],\"title_prefix\":\"[rig-sampler] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
+ GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
+
+ update_cache_memory:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: always() && needs.detection.result == 'success' && needs.agent.result == 'success'
+ runs-on: ubuntu-slim
+ permissions: {}
+ env:
+ GH_AW_WORKFLOW_ID_SANITIZED: dailyrigsampler
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.5
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Rig Sampler"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-rig-sampler.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download cache-memory artifact (default)
+ id: download_cache_default
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ continue-on-error: true
+ with:
+ name: cache-memory
+ path: /tmp/gh-aw/cache-memory
+ - name: Check if cache-memory folder has content (default)
+ id: check_cache_default
+ shell: bash
+ run: |
+ if [ -d "/tmp/gh-aw/cache-memory" ] && [ "$(ls -A /tmp/gh-aw/cache-memory 2>/dev/null)" ]; then
+ echo "has_content=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "has_content=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Save cache-memory to cache (default)
+ if: steps.check_cache_default.outputs.has_content == 'true'
+ uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
+ path: /tmp/gh-aw/cache-memory
+
diff --git a/.github/workflows/daily-rig-sampler.md b/.github/workflows/daily-rig-sampler.md
new file mode 100644
index 0000000..5e19224
--- /dev/null
+++ b/.github/workflows/daily-rig-sampler.md
@@ -0,0 +1,97 @@
+---
+name: Daily Rig Sampler
+description: >
+ Each day, pick one rig sample (cached round-robin) from src/samples/,
+ run it with the rig skill, analyze harness performance, and apply one
+ focused quick-win improvement to skills/rig/rig.ts in a new draft PR.
+on:
+ schedule: daily
+ workflow_dispatch:
+permissions:
+ contents: read
+ actions: read
+ issues: read
+ pull-requests: read
+engine: copilot
+strict: true
+timeout-minutes: 30
+tools:
+ github:
+ mode: gh-proxy
+ toolsets: [default]
+ bash: ["*"]
+ edit:
+ cache-memory: true
+network:
+ allowed: [defaults, github, node]
+safe-outputs:
+ create-pull-request:
+ title-prefix: "[rig-sampler] "
+ labels: [automation, ai-agent]
+ draft: true
+ reviewers: [copilot]
+ allowed-files:
+ - "skills/rig/rig.ts"
+---
+
+## Task
+
+You are improving the rig TypeScript harness one focused change at a time.
+
+### Step 1 — Pick the next sample (cached round-robin)
+
+1. Run `ls src/samples/` to list all sample files sorted alphabetically.
+2. Open the cache-memory file `/tmp/gh-aw/cache-memory/last-sample.json`.
+ - If it does not exist, start from the first file.
+ - Otherwise read the `lastFile` field and advance to the next file in the sorted list (wrapping around).
+3. Write `{"lastFile": ""}` back to `/tmp/gh-aw/cache-memory/last-sample.json`.
+4. Note the chosen sample file path, e.g. `src/samples/05-write-readme-intent.ts`.
+
+### Step 2 — Install dependencies and run the sample
+
+1. Run `npm install` in the repository root to ensure all dependencies are present.
+2. Run the chosen sample through the rig skill launcher:
+ ```
+ node skills/rig/rig.ts 2>&1
+ ```
+ Capture both stdout (the agent's structured JSON output) and stderr (JSONL event lines prefixed with `rig.copilot-ask`).
+3. Record the full output for analysis.
+
+### Step 3 — Analyze harness performance
+
+With the captured run output, evaluate:
+
+- **Did it succeed?** Did the agent return valid JSON matching the declared output schema, or did it fail?
+- **Repair turns**: Count how many times the harness had to retry due to invalid JSON or schema violations (`rig.copilot-ask` events with `turns > 1`).
+- **Turn count and latency**: Note total turns from the JSONL events.
+- **Schema fit**: Did the sample's output schema feel too loose (e.g. plain `s.string` where a structured type would help) or too strict (repair loops due to enum mismatches)?
+- **Error messages**: Were any error messages unclear or unhelpful?
+- **API ergonomics**: Was there anything awkward in how the sample had to express its intent — boilerplate that a helper could eliminate, or a missing convenience on `p.*` or `s.*`?
+
+### Step 4 — Read rig.ts
+
+Read `skills/rig/rig.ts` in full to understand the current implementation.
+
+### Step 5 — Identify one quick-win improvement
+
+Based on your analysis of the actual run, identify **exactly one** small, self-contained improvement to `skills/rig/rig.ts`.
+
+Good categories (pick the one most directly supported by the run evidence):
+- A missing `s.*` schema helper that would simplify sample code or prevent a repair loop.
+- A clearer error message surfaced during repair or schema validation.
+- A JSDoc comment on a public export that is currently undocumented.
+- A small type-safety improvement (stricter overload, narrower generic).
+- A minor performance or usability tweak with no behaviour change.
+
+Do **not** change the public API in a breaking way. Keep the change small and reviewable.
+
+### Step 6 — Apply the improvement
+
+Edit `skills/rig/rig.ts` to implement the improvement. Use the `edit` tool.
+
+### Step 7 — Create a pull request
+
+Emit a `create-pull-request` output with:
+- `title`: one-line description of the improvement (no prefix needed — it is added automatically).
+- `body`: explain which sample was run, what the run revealed (repair turns, output quality, etc.), and why this change improves the harness.
+- `branch`: `rig-sampler/` (e.g. `rig-sampler/05-write-readme-intent`).
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..d4946b6
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,107 @@
+name: Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ release_type:
+ description: "Release type"
+ required: true
+ type: choice
+ options:
+ - patch
+ - minor
+ - major
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ # Only run on the origin repo (not forks)
+ if: github.repository == 'pelikhan/rig'
+ permissions:
+ contents: write
+
+ steps:
+ - name: Check actor permission
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ username: context.actor,
+ });
+ const level = perm.permission;
+ if (level !== 'admin' && level !== 'maintain') {
+ core.setFailed(`Actor ${context.actor} has permission '${level}', which is insufficient. Admin or maintain required.`);
+ }
+
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ fetch-depth: 0
+
+ - name: Use Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: 24
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Typecheck
+ run: npm run typecheck
+
+ - name: Compute next version
+ id: semver
+ run: |
+ # Get the latest semver tag (vX.Y.Z), default to v0.0.0 if none exists
+ LAST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -n 1)
+ if [ -z "$LAST_TAG" ]; then
+ LAST_TAG="v0.0.0"
+ fi
+ echo "Last tag: $LAST_TAG"
+
+ # Strip leading 'v'
+ VERSION="${LAST_TAG#v}"
+ MAJOR=$(echo "$VERSION" | cut -d. -f1)
+ MINOR=$(echo "$VERSION" | cut -d. -f2)
+ PATCH=$(echo "$VERSION" | cut -d. -f3)
+
+ RELEASE_TYPE="${{ github.event.inputs.release_type }}"
+ case "$RELEASE_TYPE" in
+ major)
+ MAJOR=$((MAJOR + 1))
+ MINOR=0
+ PATCH=0
+ ;;
+ minor)
+ MINOR=$((MINOR + 1))
+ PATCH=0
+ ;;
+ patch)
+ PATCH=$((PATCH + 1))
+ ;;
+ esac
+
+ NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}"
+ echo "New tag: $NEW_TAG"
+ echo "tag=$NEW_TAG" >> "$GITHUB_OUTPUT"
+
+ - name: Create and push tag
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git tag "${{ steps.semver.outputs.tag }}"
+ git push origin "${{ steps.semver.outputs.tag }}"
+
+ - name: Create GitHub Release
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ await github.rest.repos.createRelease({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ tag_name: '${{ steps.semver.outputs.tag }}',
+ name: '${{ steps.semver.outputs.tag }}',
+ generate_release_notes: true,
+ });
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c2658d7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..dbd4bd7
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "github.copilot.enable": {
+ "markdown": true
+ }
+}
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..604dc23
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,61 @@
+# AGENTS.md
+
+## Project Overview
+
+Rig is a minimal TypeScript agent harness. The core runtime (`skills/rig/rig.ts`) provides declarative agent construction with typed input/output shapes, prompt intents, and a Copilot SDK runtime.
+
+## Architecture
+
+```
+skills/rig/rig.ts — Core runtime (agent, p, copilotEngine, schemas)
+skills/rig/samples/ — 51 sample agents demonstrating patterns
+src/engines/copilot.test.ts — Copilot engine unit tests (vitest)
+src/rig.test.ts — Unit tests (vitest)
+scripts/run-sample.test.ts — Sample runner with a stub Copilot SDK client (dry-run)
+skills/rig/SKILL.md — Framework reference docs
+```
+
+All imports use the `"rig"` path alias (resolved via tsconfig paths + vitest alias). `copilotEngine` is exported directly from `rig` for client construction.
+
+## Commands
+
+| Task | Command |
+|------|---------|
+| Typecheck | `npm run typecheck` |
+| Unit tests | `npm test` |
+| Run samples (stub) | `npm run sample` |
+| Run single sample (stub) | `RIG_SAMPLE=02 npm run sample` |
+| Run a sample for real | `echo "" \| node skills/rig/rig.ts ` (`npm run sample:run`) |
+
+## Code Style
+
+- Keep the core (`skills/rig/rig.ts`) self-contained; `@github/copilot-sdk` is imported directly in `skills/rig/rig.ts`
+- Minimal comments; code should be self-explanatory
+- Use `node:` prefix for Node.js built-in imports
+- Types are colocated with the module that defines them, not in separate `.d.ts` files
+- Trailing underscore on object keys (`key_`) means optional field
+- Do not add legacy compatibility bridges; update callers, samples, and docs to the current API
+
+## Testing
+
+- Framework: vitest
+- Tests live in `src/rig.test.ts` (agent definition, invocation, validation, and prompt intent coverage)
+- Stub the Copilot SDK client with `vi.mock("@github/copilot-sdk", ...)`
+- All unit tests must pass before committing
+- Samples run via a stub Copilot SDK client that synthesizes shape-conforming output from the prompt's `` block
+
+## Key Concepts
+
+- **Shape descriptors**: JS values used as type exemplars (e.g., `""` = string, `0` = number, `[""]` = string array). Promoted to schemas via `SchemaLike`.
+- **Schema helpers (`s.*`)**: `s.string`, `s.number`, `s.boolean`, `s.unknown`, `s.array`, `s.object`, `s.record`, `s.enum`, `s.optional`
+- **Prompt intents (`p.*`)**: `p.bash(cmd)`, `p.read(path)`, `p.write(path, content)` — declarative placeholders resolved into prompt instructions, not executed in-process
+- **Prompts**: `p\`...\`` template tag composes instructions with inline `p.*` helpers
+- **Runtime transport**: Copilot SDK sessions are created by the harness; use launcher `--server` to switch to stdio transport.
+- **Repair**: built-in addon re-prompts on parse/validation failure up to `maxTurns`, and other addons can still steer retry prompts.
+
+## Sample guide
+
+- `20-issue-reproducer.ts` — chained diagnosis, fix planning, and review
+- `36-subagent-delegation.ts` — focused-agent delegation
+- `47-prompt-intents.ts` — prompt intent primitives
+- `50-end-to-end-release-agent.ts` — end-to-end release workflow orchestration
diff --git a/README.md b/README.md
index 88d6667..6377eea 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,272 @@
# rig
-Harness as a Skill
+
+`rig` is a minimal TypeScript agent harness skill designed to run inside sandboxed agentic workflows embedded in markdown.
+
+## Install
+
+```bash
+npm install github:pelikhan/rig#v0.0.8
+```
+
+Or install the skill for Copilot coding agent:
+
+```bash
+gh skills clone pelikhan/rig
+```
+
+## Core API
+
+```ts
+import {
+ agent,
+ defineTool,
+ p,
+ s,
+} from "rig";
+import { addons, oncePerSession, repair, steering, timeout } from "rig/addons";
+```
+
+- `agent(spec)` creates a typed agent function.
+- `s.*` defines input/output schemas. Omit `input`/`output` when free-form strings are enough.
+- `p.*` creates declarative prompt intents for prompt templates or inputs.
+- `p()` and ``p`...` `` create a prompt builder with `var`, `write`, and `region` primitives for assembling prompts.
+- ``p`...` `` also works in `instructions` to embed prompt intents directly: `` instructions: p`Review ${p.bash("git status")}` ``.
+- `defineTool(name, config)` matches the Copilot SDK helper and accepts rig `s.*` schemas for `parameters`.
+- `addons` accepts express-like `(context, next)` turn addons for steering, inline validation, and Copilot session access.
+- `rig` starts with no default addons.
+- `rig/addons` provides optional addon helpers: `oncePerSession`, `repair`, `steering`, `timeout`, and `addons.{oncePerSession,repair,steering,timeout}`.
+- `p\`...\`` returns a prompt builder and renders intent values when coerced to string; prefer `${p.read(...)}` / `${p.bash(...)}` when the context source is already known.
+
+## Embedding in markdown
+
+Use `rig` code fences in markdown files to define runnable harness programs:
+
+````markdown
+```rig
+export default "Summarize this repository.";
+```
+````
+
+Extract the `rig` fence and run it with:
+
+```bash
+awk '/^```rig$/{in_block=1;next}/^```$/{if(in_block){exit}}in_block' ./program.md | node skills/rig/rig.ts
+```
+
+## Quick start
+
+```ts
+import { agent, p, s } from "rig";
+
+// Agent role: extract package scripts and summarize what they do.
+const extractScripts = agent({
+ model: "nano",
+ instructions: p`Read ${p.read("package.json")} and summarize the package scripts. Use ${p.bash("find src -name '*.ts' -type f | sort")} only to call out source files that look relevant.`,
+ output: s.object({
+ scriptsByName: s.record(s.string),
+ summary: s.string,
+ relatedFiles: s.array(s.string),
+ }),
+});
+
+export default extractScripts;
+```
+
+When the context already lives in the workspace, prefer intent templates like the example above over adding `input` fields just to shuttle shell output or file contents. Favor `p.read("path")` over `p.bash("cat path")`, and let the harness work from files instead of assembling large in-memory strings first.
+
+## Schemas
+
+```ts
+s.string
+s.string("description")
+s.number
+s.boolean
+s.unknown
+s.array(item, "description")
+s.object(fields, "description")
+s.record(value, "description")
+s.enum(...values)
+s.enum(values, "description")
+s.optional(shape)
+s.optional(shape, "description")
+```
+
+Use declarative `s.*` helpers for every schema node.
+`s.*` values serialize directly as JSON Schema, so no separate conversion step is needed.
+Rig renders these declarations as JSON Schema in prompt schema blocks.
+Implicit object literals, trailing-underscore optional fields, and `{"*": ...}` record sugar are not supported.
+
+## Prompt intents
+
+Prompt intents for shell and file operations are optimized for sandboxed agentic workflows. They assume the harness is already running with the required constraints and protections, so the generated instructions tell the agent to execute the action directly instead of adding extra permission prompts.
+
+```ts
+p.bash("git status --short")
+p.bash("npm test")
+p.read("README.md")
+p.write("README.md", "# Updated\n")
+
+const reviewWorkspace = agent({
+ instructions: p`Review ${p.read("README.md")} against ${p.bash("git status --short")}.`,
+});
+```
+
+```ts
+const b = p();
+const repo = b.var("repo", "rig");
+b.write("Summarize repository ", repo, ".\n");
+b.write("Start by checking ", b.bash("git status --short"), ".\n");
+b.region("ts", "type Summary = { text: string };");
+const prompt = b.toString();
+```
+
+## Tools
+
+Register custom tools directly on an agent using the same shape as `@github/copilot-sdk`:
+
+```ts
+const lookupIssue = defineTool("lookup_issue", {
+ description: "Look up an issue by id.",
+ parameters: s.object({
+ issue: s.string,
+ }),
+ handler: async ({ issue }) => `Issue ${issue}`,
+});
+
+const triage = agent({
+ instructions: "Use lookup_issue before answering.",
+ tools: [lookupIssue],
+});
+```
+
+Rig defaults agent tools to `skipPermission: true`, and you can also place plain tool objects in `tools`; rig will convert `s.*` parameter schemas into JSON Schema before creating the Copilot session.
+
+## Evaluating agentic performance
+
+Use these samples to quickly gauge how well `rig` supports increasingly agentic workflows:
+
+- `skills/rig/samples/20-issue-reproducer.md` — chained diagnose/fix flow
+- `skills/rig/samples/36-subagent-delegation.md` — delegation between focused agents
+- `skills/rig/samples/47-prompt-intents.md` — prompt intents embedded directly in prompt templates
+- `skills/rig/samples/50-end-to-end-release-agent.md` — multi-step release planning workflow
+- `docs/rig-syntax-genaiscript-comparison.md` — comparison of rig syntax with 10 GitHub GenAIScript sample ports
+
+## Agent behavior
+
+- Default model: `gpt-4.1`
+- Default max turns: `4`
+- No addons are loaded by default (including repair/retry behavior)
+
+Per call, you can override `model`, `timeout`, `maxTurns`, and `signal`.
+
+## Addons
+
+Each agent call runs a per-turn addon chain:
+
+```ts
+const steerFinalTurn = async (context, next) => {
+ await next();
+ if (context.nextPrompt && context.turn === context.maxTurns - 1) {
+ context.nextPrompt = `${context.nextPrompt}\nYou are running out of turns. Return corrected JSON now.`;
+ }
+};
+
+const review = agent({
+ maxTurns: 3,
+ addons: steerFinalTurn,
+});
+```
+
+`context` includes `prompt`, `response`, `turn`, `maxTurns`, `signal`, `output`, `nextPrompt`, `error`, and `completed`.
+
+For the common retry flow with last-turn steering or stable default timeouts, opt into addons:
+
+```ts
+const review = agent({
+ maxTurns: 3,
+ addons: [timeout({ timeout: 30_000 }), steering(), repair],
+});
+```
+
+For direct SDK access, use `oncePerSession(...)` to register with the session once:
+
+```ts
+const review = agent({
+ addons: oncePerSession((session) => {
+ session.on?.((event) => {
+ // custom event handling
+ });
+ }),
+});
+```
+
+Per-turn addons still receive `context.session` directly, and you can also register addons after creating the agent:
+
+```ts
+const timingAddon = async (context, next) => {
+ await next();
+};
+
+const review = agent({});
+review.use(timingAddon);
+```
+
+## Copilot SDK runtime
+
+`rig` is specialized for Copilot SDK sessions inside sandboxed agentic workflows.
+
+By default it connects to an already-running Copilot server via HTTP (`COPILOT_SDK_URI`, then `localhost:7777`).
+Pass `--server` to spawn the server over stdio when launching a program.
+Run `node skills/rig/rig.ts --help` for CLI usage; the launcher also accepts common help aliases such as `-h`, `help`, `/help`, and `/?`.
+
+For runnable programs, you can pipe a rig program directly on stdin (assumes the Copilot server is already running):
+
+```bash
+cat <<'RIG' | node skills/rig/rig.ts
+import { p } from "rig";
+export default p`Summarize this repository and include highlights from ${p.read("README.md")}.`;
+RIG
+```
+
+Inline stdin programs run a default-exported root program with no required external input and write the result to stdout. Export either an agent, a string, or a prompt builder. If `export default` is omitted, the harness defaults to the first `const/let/var name = agent(...)` assignment.
+`import { agent, p, s } from "rig"` is optional in inline mode because the harness injects it when missing.
+
+Inline mode accepts root agents that either omit `input`, use `input: s.object({})`, or rely on the default `input: s.string` (which is invoked with `""`).
+
+Pass `--server` to start the Copilot server automatically as part of the run:
+
+```bash
+cat ./program.ts | node skills/rig/rig.ts --server
+```
+
+Pass `--typecheck` to typecheck the rig program before execution:
+
+```bash
+cat ./program.ts | node skills/rig/rig.ts --typecheck
+```
+
+To run a root program from a program file, export the root value as the default export and pass input on stdin:
+
+```bash
+echo "Summarize this repository" | node skills/rig/rig.ts src/program.ts
+```
+
+Program-file mode also supports `--typecheck`:
+
+```bash
+echo "Summarize this repository" | node skills/rig/rig.ts src/program.ts --typecheck
+```
+
+For program-file mode stdin coercion:
+- if root input schema is `string`, stdin is passed as raw text
+- if root input schema is an object containing `text`, stdin is passed as `{ text: "" }`
+- otherwise stdin must be valid JSON for the declared input schema
+
+Copilot SDK lifecycle events and rig request events are logged to stderr as JSONL.
+
+## Local development
+
+```bash
+npm test
+npm run typecheck
+```
diff --git a/docs/rig-samples-agent-pov-review.md b/docs/rig-samples-agent-pov-review.md
new file mode 100644
index 0000000..c1550a6
--- /dev/null
+++ b/docs/rig-samples-agent-pov-review.md
@@ -0,0 +1,57 @@
+# Rig samples review from an agent POV
+
+This review critiques sample design, syntax, and intuitiveness across `src/samples/*` and `skills/rig/samples/*`.
+
+## What works well
+
+1. **Small core surface area**
+ The `agent + s + p` API is compact and repeatable, which makes generation predictable once the pattern is learned.
+
+2. **Strong output contracts**
+ Most samples use explicit `s.object(...)` outputs and enums, which is friendly to repair loops and machine consumption.
+
+3. **Real task coverage**
+ The catalog covers practical workflows (triage, CI diagnosis, release planning, delegation) instead of toy prompts.
+
+## Design and intuitiveness issues
+
+1. **Root-agent mental model is unclear in multi-agent examples**
+ Several orchestrated samples run multiple agents but export a subagent as default (`20-issue-reproducer.ts`, `36-subagent-delegation.ts`, `50-end-to-end-release-agent.ts`, `51-claude-design.ts`, `53-ralf-loop.ts`).
+ For an agent author, this makes "what is the runnable root?" ambiguous.
+
+2. **Sample style drifts from documented guidance**
+ Docs recommend `p.read(...)` when reading files, but samples still frequently use `p.bash("cat ...")` (`23-schema-inference.ts`, `41-parse-coverage.ts`, and others).
+ This weakens trust in the docs as the canonical source.
+
+3. **Verbose schema ceremony for simple tasks**
+ The explicit schema style is correct, but for quick one-shot tasks the amount of typing can feel heavier than direct SDK usage.
+
+4. **Inline skill sample trust is currently broken by a failing example**
+ `skills/rig/samples/52-claude-design.md` exports a root agent that requires input, but stdin inline mode expects no-input root agents.
+ A failing checked-in sample reduces confidence for both humans and agents.
+
+## Syntax critique
+
+- **Good:** declarative schemas and explicit enums reduce ambiguity.
+- **Good:** `p\`\`` with inline intents is expressive for agent-authored prompts.
+- **Rough edge:** syntax is concise at runtime but still repetitive in examples with many tiny helper agents.
+- **Rough edge:** mixed formatting styles across samples (some compact, some highly expanded) make pattern extraction harder for generation agents.
+
+## Would an agent choose `rig` or Copilot SDK directly?
+
+### Agent would likely choose `rig` when
+
+- the task is schema-first and expects strict JSON output;
+- shell/file context can be expressed with `p.read/p.bash` intents;
+- the goal is to generate a runnable sample quickly with consistent scaffolding.
+
+### Agent would likely choose Copilot SDK directly when
+
+- orchestration needs custom session lifecycle control, transport, or event handling;
+- the workflow is long-running and not naturally expressed as one root `agent(...)` contract;
+- the task needs non-standard control flow where harness conventions become constraints.
+
+## Bottom line
+
+`rig` is more attractive than direct Copilot SDK for most sample-style, structured tasks because it compresses boilerplate into a predictable pattern.
+However, sample inconsistencies (root export ambiguity, file-read style drift, and one failing markdown sample) currently make the experience feel less intuitive than it could be.
diff --git a/docs/rig-syntax-copilot-pi-agent-comparison.md b/docs/rig-syntax-copilot-pi-agent-comparison.md
new file mode 100644
index 0000000..5df8c5e
--- /dev/null
+++ b/docs/rig-syntax-copilot-pi-agent-comparison.md
@@ -0,0 +1,57 @@
+# Rig syntax comparison: Copilot SDK APIs and pi-agent SDK
+
+This page compares how `rig` syntax maps to two other harness styles:
+
+- GitHub Copilot SDK APIs (API usage only)
+- pi-agent SDK
+
+The focus is generation reliability: what an agent can produce quickly with low ambiguity.
+
+## 1) Syntax mapping overview
+
+| Rig concept | Copilot SDK APIs (only) | pi-agent SDK |
+|---|---|---|
+| `agent({ name, instructions, input, output })` | App-level session setup + prompt contract + response parsing/validation | Agent definition/config + prompt contract + response parsing/validation |
+| `s.object(...)`, `s.enum(...)`, `s.array(...)` | Explicit JSON schema or prompt-constrained JSON validated in app code | Same pattern: schema-constrained JSON validated by the harness/app |
+| `p.read(...)`, `p.bash(...)` | Tool/context calls orchestrated by the host app before/within turns | Tool/context calls via pi-agent tool integration/orchestration |
+| `agents: { subagent }` | Multiple sessions/roles coordinated in app orchestration | Multi-agent graph/delegation orchestration |
+| `maxTurns`, optional `rig/addons` repair addon | Explicit retry + repair loop in app logic | Retry/repair policies in agent workflow/harness |
+| `permissions` | Host-side policy gates around shell/write operations | Host-side tool permission policies |
+
+## 2) Top 10 scenarios: Copilot SDK APIs (ranked easiest → hardest)
+
+| Rank | Scenario | Ease/confusion rationale | Rig → Copilot SDK mapping note |
+|---:|---|---|---|
+| 1 | Single-field summarizer | Minimal schema and deterministic shape keep ambiguity very low. | `output: { text }` maps to one session request and strict JSON parse. |
+| 2 | Enum classifier | Closed label sets reduce drift and invalid output. | `s.enum(...)` maps to constrained JSON contract validated after response. |
+| 3 | Structured extractor | Input text to typed fields is usually straightforward. | `input/output` schemas map to one-turn extraction with validation. |
+| 4 | Diff summary | Slightly larger context but objective remains clear. | Diff as input + structured summary fields in response contract. |
+| 5 | Test-log diagnosis | Interpretation complexity rises with noisy logs. | Log input + typed root-cause/next-step schema with validation. |
+| 6 | PR triage recommendation | Requires prioritization judgment and policy interpretation. | One/two turns with constrained triage schema and confidence fields. |
+| 7 | README draft generation | Creative synthesis adds style and completeness ambiguity. | Multi-section structured output with post-parse checks. |
+| 8 | Release notes generation | Requires grouping/dedup across many commits. | Batched commit input + grouped typed output contract. |
+| 9 | Schema-repairing extractor | Needs robust retry when output is invalid or partial. | `maxTurns` plus optional `rig/addons` repair addon maps to explicit app-level validation/repair loop. |
+| 10 | Multi-agent orchestrator | Highest coordination overhead across roles and merges. | `agents` maps to multi-session orchestration and aggregation logic. |
+
+## 3) Top 10 scenarios: pi-agent SDK (ranked easiest → hardest)
+
+| Rank | Scenario | Ease/confusion rationale | Rig → pi-agent mapping note |
+|---:|---|---|---|
+| 1 | Issue classification | Tight label set and simple context produce stable output. | `s.enum(...)` maps to constrained classification response schema. |
+| 2 | Package script extraction | Deterministic source file extraction is low ambiguity. | `${p.read("package.json")}` maps to file-read tool + typed extraction. |
+| 3 | Git diff summary | Clear input artifact, moderate reasoning complexity. | `${p.bash("git diff -- .")}` maps to shell tool + structured summary. |
+| 4 | PR triage | Policy interpretation exists but remains bounded by schema. | Enum-heavy triage schema maps well to classifier-style flows. |
+| 5 | Release notes draft | Cross-item synthesis introduces grouping ambiguity. | Multi-input retrieval + typed release-note sections. |
+| 6 | Security scan review | Severity calibration and false-positive handling increase confusion risk. | Findings ingestion + risk rubric in structured output schema. |
+| 7 | CI log diagnosis | Noisy traces and multiple plausible root causes. | Tooled log capture + hypothesis/next-action typed fields. |
+| 8 | Flaky test analysis | Nondeterminism and weak signals increase uncertainty. | Repeated evidence aggregation + hypothesis confidence fields. |
+| 9 | Subagent delegation planner | Coordination and dependency ordering are hard to generate correctly. | `agents` maps to multi-agent planning/delegation graph. |
+| 10 | End-to-end release agent | Long chained workflow with coupled decisions and tools. | Orchestrated multi-step workflow with strict stage outputs. |
+
+## 4) Practical takeaways for lower-confusion generation
+
+1. Start with schema-tight, single-turn tasks first.
+2. Prefer enums/literals for decision fields.
+3. Keep context acquisition explicit (`read`/`bash`/tool calls) before generation.
+4. Add repair loops only when necessary; they increase implementation complexity.
+5. Introduce subagents last, after single-agent schema contracts are stable.
diff --git a/docs/rig-syntax-genaiscript-comparison.md b/docs/rig-syntax-genaiscript-comparison.md
new file mode 100644
index 0000000..93bb49e
--- /dev/null
+++ b/docs/rig-syntax-genaiscript-comparison.md
@@ -0,0 +1,86 @@
+# Rig syntax comparison: GenAIScript `.genai.js` samples
+
+This review compares current `rig` syntax with 10 representative `.genai.js` samples found on GitHub on 2026-05-31.
+The set mixes Microsoft-owned and community repositories that use GenAIScript's `script(...)`, `$`` prompt, `def(...)`, `defSchema(...)`, `defAgent(...)`, and `env/workspace/github` conventions.
+
+## Syntax mapping
+
+| GenAIScript pattern | Rig pattern | Notes |
+|---|---|---|
+| `script({ ... })` | `agent({ ... })` | `rig` puts name, model, instructions, schemas, and subagents in one declaration. |
+| `$` template prompt | `instructions: p\`...\`` or a plain string | Both are readable; `rig` keeps prompt text inside the agent spec. |
+| `def("NAME", value)` | `${p.read(...)}`, `${p.bash(...)}`, or inline prompt text | `rig` favors explicit prompt intents over mutable prompt variables. |
+| `defSchema(...)` with JSON Schema | `s.object(...)`, `s.array(...)`, `s.enum(...)`, `s.optional(...)` | `rig` uses one explicit JSON Schema-compatible declaration style across samples and runtime. |
+| `defAgent(...)` | `agents: { helper }` | `rig` subagents are declared as normal agents and attached structurally. |
+| `workspace.*`, `github.*`, `env.vars.*` | prompt intents plus caller-provided input | `rig` exposes less ambient runtime state and pushes more context into the prompt contract. |
+| top-level `await` workflow code | one declarative agent spec | `rig` is smaller and easier to generate, but less imperative for long scripted workflows. |
+
+## Reviewed samples and rig ports
+
+| Sample | Source | Rig port |
+|---|---|---|
+| glossary generation | [`glossary.genai.js`](https://raw.githubusercontent.com/microsoft/generative-ai-with-javascript/a7abd828d0a7b5d56f6e5450e5b26b250c33a392/docs/scripts/glossary.genai.js) | [`skills/rig/samples/55-genaiscript-glossary-port.md`](../skills/rig/samples/55-genaiscript-glossary-port.md) |
+| batch refactor with helper agents | [`refactor.genai.js`](https://raw.githubusercontent.com/sinedied/genaiscript-talk/882eb643d6fcb854d22b939a870b0d0dd53d36da/genaisrc/refactor.genai.js) | [`skills/rig/samples/56-genaiscript-refactor-batch-port.md`](../skills/rig/samples/56-genaiscript-refactor-batch-port.md) |
+| GitHub issue review | [`issue-review.genai.js`](https://raw.githubusercontent.com/sinedied/genaiscript-talk/882eb643d6fcb854d22b939a870b0d0dd53d36da/genaisrc/issue-review.genai.js) | [`skills/rig/samples/57-genaiscript-issue-review-port.md`](../skills/rig/samples/57-genaiscript-issue-review-port.md) |
+| multi-agent travel plan | [`travel.genai.js`](https://raw.githubusercontent.com/sinedied/genaiscript-talk/882eb643d6fcb854d22b939a870b0d0dd53d36da/genaisrc/travel.genai.js) | [`skills/rig/samples/58-genaiscript-travel-plan-port.md`](../skills/rig/samples/58-genaiscript-travel-plan-port.md) |
+| file-backed city extraction | [`cityinfo.genai.js`](https://raw.githubusercontent.com/darbotlabs/genaid/46c02f2e23082cb9c244483c9f36dd39212101b2/packages/sample/genaid/cityinfo.genai.js) | [`skills/rig/samples/59-genaiscript-city-info-port.md`](../skills/rig/samples/59-genaiscript-city-info-port.md) |
+| schema-only city generator | [`defschema.genai.js`](https://raw.githubusercontent.com/darbotlabs/genaid/46c02f2e23082cb9c244483c9f36dd39212101b2/packages/sample/genaid/defschema.genai.js) | [`skills/rig/samples/60-genaiscript-schema-cities-port.md`](../skills/rig/samples/60-genaiscript-schema-cities-port.md) |
+| workspace file picker | [`list-files.genai.js`](https://raw.githubusercontent.com/darbotlabs/genaid/46c02f2e23082cb9c244483c9f36dd39212101b2/packages/sample/genaid/list-files.genai.js) | [`skills/rig/samples/61-genaiscript-list-files-port.md`](../skills/rig/samples/61-genaiscript-list-files-port.md) |
+| TODO implementation helper | [`todo.genai.js`](https://raw.githubusercontent.com/darbotlabs/genaid/46c02f2e23082cb9c244483c9f36dd39212101b2/packages/sample/genaid/todo.genai.js) | [`skills/rig/samples/62-genaiscript-todo-port.md`](../skills/rig/samples/62-genaiscript-todo-port.md) |
+| slide deck generator | [`slides.genai.js`](https://raw.githubusercontent.com/darbotlabs/genaid/46c02f2e23082cb9c244483c9f36dd39212101b2/packages/sample/genaid/slides.genai.js) | [`skills/rig/samples/63-genaiscript-slide-deck-port.md`](../skills/rig/samples/63-genaiscript-slide-deck-port.md) |
+| code review persona | [`review-code.genai.js`](https://raw.githubusercontent.com/sinedied/grumpydev-mcp/7310acbda1b82a41e2f6b31bd064f612f169d6fc/genaisrc/review-code.genai.js) | [`skills/rig/samples/64-genaiscript-grumpy-review-port.md`](../skills/rig/samples/64-genaiscript-grumpy-review-port.md) |
+
+## JSON schema syntax review of the ports
+
+All 10 ports are now reviewed against the current rig schema style (explicit `s.*` declarations that serialize to JSON Schema):
+
+1. **55 glossary**: uses `s.object` with nested `s.array(s.object(...))` for strict term/definition pairs.
+2. **56 refactor batch**: both root and helper agents use explicit object schemas for deterministic subagent handoff.
+3. **57 issue review**: output shape is explicit (`summary` + `questions`) and consistent with prompt constraints.
+4. **58 travel plan**: helper-agent and root-agent outputs are all explicitly typed with `s.object` + `s.array`.
+5. **59 city info**: uses `s.array(s.object(...))` to enforce strongly typed tabular extraction.
+6. **60 schema cities**: mirrors 59 and keeps the generated records schema-first.
+7. **61 list files**: simple but explicit array-of-strings contract for stable tooling consumption.
+8. **62 TODO**: nested object-array schema keeps file/plan entries constrained and machine-readable.
+9. **63 slide deck**: deep nested schema (`slides[].title/bullets[]`) is explicit and unambiguous.
+10. **64 grumpy review**: fixed shape (`findings[]`, `verdict`) keeps persona output structured.
+
+The reviewed set no longer relies on deprecated shorthand forms; it consistently uses canonical `s.*` schema declarations.
+
+## What GenAIScript makes easy
+
+1. Very fast top-level scripting with ambient helpers such as `workspace`, `github`, and `env`.
+2. Natural imperative flows: find files, read them, call the model, then write output.
+3. JSON Schema reuse through `defSchema(...)` without translating into another schema DSL.
+4. Prompt assembly is concise when a script mostly glues inputs into one `$`` block.
+
+## What rig makes easier
+
+1. One canonical declaration shape: `agent({ name, model, instructions, output, agents })`.
+2. Smaller syntax surface for generated code: `agent`, `s`, and `p` cover most examples.
+3. Stronger schema readability for common cases because `s.object(...)` and `s.enum(...)` stay compact.
+4. Cleaner separation between prompt contract and host-side side effects.
+5. Better sample consistency because subagents use the same syntax as root agents.
+
+## Rig weaknesses exposed by the comparison
+
+1. File discovery is awkward compared with `workspace.findFiles(...)`; ports fall back to shell commands inside `p.bash(...)`.
+2. There is no first-class equivalent to ambient `env.vars` sample parameters in inline markdown examples, so some ports must inline example values.
+3. `rig` lacks a direct artifact-oriented pattern like GenAIScript's read/process/write script flow, which makes output-file generation examples less natural.
+4. Raw JSON Schema copy/paste from external examples still needs translation into `s.*` helpers.
+5. Long imperative workflows are harder to express directly because `rig` intentionally centers one agent spec over step-by-step runtime code.
+
+## GenAIScript weaknesses exposed by the comparison
+
+1. The ambient runtime (`env`, `workspace`, `github`, globals) is powerful but broad, which makes generation drift more likely.
+2. Sample syntax mixes metadata, prompt text, file IO, agent definitions, and side effects at top level.
+3. Reusing helper agents and schemas adds extra concepts (`def`, `defAgent`, `defSchema`) that beginners must learn quickly.
+4. Output contracts rely more often on prompt wording and JSON Schema snippets than on one obvious canonical declaration shape.
+
+## Follow-ups to improve rig
+
+1. Add a first-class `p.glob(...)` or `p.findFiles(...)` helper so workspace enumeration does not require shell `find` commands.
+2. Add a lightweight sample metadata helper for common patterns such as file-backed context, canned inputs, and generated artifact descriptions.
+3. Document a canonical "generate artifact" pattern that combines `p.read(...)`, strict output schemas, and `p.write(...)` instructions.
+4. Add one official multi-agent orchestration sample that mirrors GenAIScript's `defAgent(...)` style more directly.
+5. Document JSON Schema to `s.*` translation rules for users porting prompts from GenAIScript or similar tools.
diff --git a/docs/rig-syntax-review.md b/docs/rig-syntax-review.md
new file mode 100644
index 0000000..0b2969a
--- /dev/null
+++ b/docs/rig-syntax-review.md
@@ -0,0 +1,40 @@
+# Rig syntax review
+
+This review covers the current rig syntax as expressed in:
+
+- `src/rig.ts`
+- `skills/rig/SKILL.md`
+- all sample programs in `src/samples/`
+
+## Current state
+
+Rig now enforces a single declarative schema style:
+
+- use `s.object(...)` for object shapes
+- use `s.array(...)` for arrays
+- use `s.record(...)` for records
+- use `s.optional(...)` for optional fields
+- use `s.enum(...)` explicitly for union values
+
+Implicit schema syntax has been removed from the runtime and sample corpus.
+Shorthand object literals, trailing-underscore optional fields, and `{"*": ...}` record sugar are no longer accepted.
+
+## What works well
+
+1. **One canonical schema dialect.** The runtime, samples, and skill documentation all reinforce the same `s.*` style.
+2. **Small syntax surface.** `agent`, `s`, `p`, and call-time overrides remain enough to express the full harness.
+3. **Consistent runtime contract.** Prompt rendering still emits ``, ``, ``, and ``, with optional `` and `` blocks.
+4. **Better failure mode for invalid declarations.** Runtime validation now rejects non-declarative schemas with a clear error.
+
+## Remaining guidance
+
+1. Keep new samples in the explicit declarative style.
+2. Prefer `s.optional(...)` over any naming convention for optional fields.
+3. Treat `skills/rig/SKILL.md` and the early samples as the canonical starting points for generated code.
+4. Continue keeping the runnable sample corpus green so checked-in examples stay trustworthy.
+
+## Validation
+
+- `npm test`
+- `npm run typecheck`
+- `npm run sample`
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..a9a3f5f
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1501 @@
+{
+ "name": "rig",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "rig",
+ "devDependencies": {
+ "@github/copilot-sdk": "^1.0.0-beta.9",
+ "@types/node": "^25.9.1",
+ "typescript": "^5.8.0",
+ "vitest": "^4.1.7",
+ "zx": "^8.8.5"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@github/copilot": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.55.tgz",
+ "integrity": "sha512-wqzI0L7krORW6jDAQPx7VnInka5BYN5yVgu+dpUK4w8xP5RgnOBa6kRoXpydj/9O1ufs0k6RKRtQjsVLp52TRw==",
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "detect-libc": "^2.1.2"
+ },
+ "bin": {
+ "copilot": "npm-loader.js"
+ },
+ "optionalDependencies": {
+ "@github/copilot-darwin-arm64": "1.0.55",
+ "@github/copilot-darwin-x64": "1.0.55",
+ "@github/copilot-linux-arm64": "1.0.55",
+ "@github/copilot-linux-x64": "1.0.55",
+ "@github/copilot-linuxmusl-arm64": "1.0.55",
+ "@github/copilot-linuxmusl-x64": "1.0.55",
+ "@github/copilot-win32-arm64": "1.0.55",
+ "@github/copilot-win32-x64": "1.0.55"
+ }
+ },
+ "node_modules/@github/copilot-darwin-arm64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.55.tgz",
+ "integrity": "sha512-v59pOpA7YO8j/lpDU/1E8l1Ag0hd26hIiEzTNbzqKd7tJpvhN0XTDWDCink50wXL656XIXt8lD8i8sGeD6yPfA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "bin": {
+ "copilot-darwin-arm64": "copilot"
+ }
+ },
+ "node_modules/@github/copilot-darwin-x64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.55.tgz",
+ "integrity": "sha512-XrJ9ent/9ogLk8yNp3TMsNVW0qTRDlkw/b34VnTgbAkJCaI3UVqaqpFn60Laa6J5mOPW0/JeKIkkva+7IJdqpQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "bin": {
+ "copilot-darwin-x64": "copilot"
+ }
+ },
+ "node_modules/@github/copilot-linux-arm64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.55.tgz",
+ "integrity": "sha512-5Q46Q72/l/U8KQRcBwYjzFPNXBCPG177FTmjEVOAH0qk7w58fMUDBEpnf9n1IpxYJDWQJ5BFGtLdfYgVVtkevw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "bin": {
+ "copilot-linux-arm64": "copilot"
+ }
+ },
+ "node_modules/@github/copilot-linux-x64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.55.tgz",
+ "integrity": "sha512-KWmMCDmKJivvOyDAAe5K8r7uSlVq8aZCh20VfrVXsc4bckO6KjXY/TOagrdBNqkk5rh8v63ghBbxFdWIOvEJRA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "bin": {
+ "copilot-linux-x64": "copilot"
+ }
+ },
+ "node_modules/@github/copilot-linuxmusl-arm64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.55.tgz",
+ "integrity": "sha512-Jb5ug9Ic1pzxB2ZT1xoR8b3Ea1xnvCa4h8cBque51+TevXe6QF98vAfSUIwLe4xu+K6JKhiKEA0SD3w29Z74eA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "bin": {
+ "copilot-linuxmusl-arm64": "copilot"
+ }
+ },
+ "node_modules/@github/copilot-linuxmusl-x64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.55.tgz",
+ "integrity": "sha512-qMGIjHxKmW9q26EpoaNKWpmEVGyL/IM8ThVkh7yolDzv9lECFudPzT5yLX7f+VIiF6qWQlrQyzmamp7/fNQ2Zg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "bin": {
+ "copilot-linuxmusl-x64": "copilot"
+ }
+ },
+ "node_modules/@github/copilot-sdk": {
+ "version": "1.0.0-beta.9",
+ "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.0-beta.9.tgz",
+ "integrity": "sha512-D4yiGL4/faFCjL7bozhX7bgxt/x1wp2LZ2p9Tw+xrA5hbcLh5Be5kPen+bFA8NbVfgt1G2djDYFZlrZjXXmcBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@github/copilot": "^1.0.55-5",
+ "vscode-jsonrpc": "^8.2.1",
+ "zod": "^4.3.6"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@github/copilot-win32-arm64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.55.tgz",
+ "integrity": "sha512-TO4EJ8it6Qki7wMKYHqGUEDYmB0EAToy+pE5++OpydB6FijyQ31+/XwjvdnEFkuB4ZgPqu/6Y8hxMKucl2+FYg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "bin": {
+ "copilot-win32-arm64": "copilot.exe"
+ }
+ },
+ "node_modules/@github/copilot-win32-x64": {
+ "version": "1.0.55",
+ "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.55.tgz",
+ "integrity": "sha512-TBMiSZMz8Dhx79JeSEM+7ONGxR5NmxfiDUdySo6thVbRmjS9D8msyAP8ucTsbLBJcTFeb7vsaeObD/ujYQgDtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "bin": {
+ "copilot-win32-x64": "copilot.exe"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
+ "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.132.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz",
+ "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz",
+ "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz",
+ "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz",
+ "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz",
+ "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz",
+ "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz",
+ "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz",
+ "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz",
+ "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz",
+ "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz",
+ "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz",
+ "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz",
+ "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz",
+ "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz",
+ "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz",
+ "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
+ "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "25.9.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
+ "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": ">=7.24.0 <7.24.7"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz",
+ "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.7",
+ "@vitest/utils": "4.1.7",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz",
+ "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.7",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz",
+ "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz",
+ "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.7",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz",
+ "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.7",
+ "@vitest/utils": "4.1.7",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz",
+ "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz",
+ "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.7",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
+ "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz",
+ "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.132.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.2",
+ "@rolldown/binding-darwin-arm64": "1.0.2",
+ "@rolldown/binding-darwin-x64": "1.0.2",
+ "@rolldown/binding-freebsd-x64": "1.0.2",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.2",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.2",
+ "@rolldown/binding-linux-arm64-musl": "1.0.2",
+ "@rolldown/binding-linux-ppc64-gnu": "1.0.2",
+ "@rolldown/binding-linux-s390x-gnu": "1.0.2",
+ "@rolldown/binding-linux-x64-gnu": "1.0.2",
+ "@rolldown/binding-linux-x64-musl": "1.0.2",
+ "@rolldown/binding-openharmony-arm64": "1.0.2",
+ "@rolldown/binding-wasm32-wasi": "1.0.2",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.2",
+ "@rolldown/binding-win32-x64-msvc": "1.0.2"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+ "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.2.tgz",
+ "integrity": "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "8.0.14",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
+ "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.15",
+ "rolldown": "1.0.2",
+ "tinyglobby": "^0.2.16"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.1.18",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz",
+ "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.7",
+ "@vitest/mocker": "4.1.7",
+ "@vitest/pretty-format": "4.1.7",
+ "@vitest/runner": "4.1.7",
+ "@vitest/snapshot": "4.1.7",
+ "@vitest/spy": "4.1.7",
+ "@vitest/utils": "4.1.7",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.7",
+ "@vitest/browser-preview": "4.1.7",
+ "@vitest/browser-webdriverio": "4.1.7",
+ "@vitest/coverage-istanbul": "4.1.7",
+ "@vitest/coverage-v8": "4.1.7",
+ "@vitest/ui": "4.1.7",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/vscode-jsonrpc": {
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz",
+ "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zx": {
+ "version": "8.8.5",
+ "resolved": "https://registry.npmjs.org/zx/-/zx-8.8.5.tgz",
+ "integrity": "sha512-SNgDF5L0gfN7FwVOdEFguY3orU5AkfFZm9B5YSHog/UDHv+lvmd82ZAsOenOkQixigwH2+yyH198AwNdKhj+RA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "zx": "build/cli.js"
+ },
+ "engines": {
+ "node": ">= 12.17.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3be2c41
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "rig",
+ "private": true,
+ "type": "module",
+ "exports": {
+ ".": "./skills/rig/rig.ts",
+ "./addons": "./skills/rig/addons.ts"
+ },
+ "scripts": {
+ "test": "vitest run src/",
+ "test:integration": "vitest run scripts/haiku.integration.test.ts",
+ "sample": "vitest run scripts/run-sample.test.ts",
+ "sample:run": "node skills/rig/rig.ts",
+ "typecheck": "tsc --noEmit"
+ },
+ "devDependencies": {
+ "@github/copilot-sdk": "^1.0.0-beta.9",
+ "@types/node": "^25.9.1",
+ "typescript": "^5.8.0",
+ "vitest": "^4.1.7",
+ "zx": "^8.8.5"
+ }
+}
diff --git a/scripts/haiku.integration.test.ts b/scripts/haiku.integration.test.ts
new file mode 100644
index 0000000..868a67e
--- /dev/null
+++ b/scripts/haiku.integration.test.ts
@@ -0,0 +1,128 @@
+import { spawn } from "node:child_process";
+import { dirname, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+import { describe, expect, it } from "vitest";
+
+const token = process.env["COPILOT_GITHUB_TOKEN"];
+const itWithToken = token ? it : it.skip;
+const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const haikuSamplePath = resolve(repoRoot, "src/samples/01-single-agent-haiku.ts");
+const sonnetSamplePath = resolve(repoRoot, "src/samples/56-single-agent-sonnet.ts");
+const complexSamplePath = resolve(repoRoot, "src/samples/57-complex-integration-sonnet.ts");
+const launcherPath = resolve(repoRoot, "skills/rig/rig.ts");
+const INTEGRATION_TIMEOUT_MS = 120_000;
+
+async function runIntegrationSample(samplePath: string, input: string): Promise {
+ return new Promise((resolve, reject) => {
+ const child = spawn(
+ process.execPath,
+ [launcherPath, samplePath, "--server"],
+ {
+ cwd: repoRoot,
+ env: { ...process.env, COPILOT_GITHUB_TOKEN: token },
+ },
+ );
+
+ let stdout = "";
+ let stderr = "";
+ const timer = setTimeout(() => {
+ child.kill("SIGTERM");
+ reject(new Error("Timed out waiting for haiku integration run."));
+ }, INTEGRATION_TIMEOUT_MS);
+
+ child.stdout.on("data", (chunk) => {
+ stdout += chunk.toString();
+ });
+
+ child.stderr.on("data", (chunk) => {
+ stderr += chunk.toString();
+ });
+
+ child.on("error", (error) => {
+ clearTimeout(timer);
+ reject(error);
+ });
+
+ child.on("close", (code) => {
+ clearTimeout(timer);
+ if (code !== 0) {
+ reject(new Error(`Haiku integration run failed with exit code ${code}.\n${stderr}`));
+ return;
+ }
+ resolve(stdout);
+ });
+
+ child.stdin.end(input);
+ });
+}
+
+describe("rig runtime integration", () => {
+ itWithToken(
+ "runs a single-agent haiku sample with the real runtime",
+ async () => {
+ const stdout = await runIntegrationSample(haikuSamplePath, "autumn rain on city windows");
+ const result = JSON.parse(stdout) as { haiku: string };
+ expect(typeof result.haiku).toBe("string");
+ const lines = result.haiku
+ .split(/\r?\n/g)
+ .map((line) => line.trim())
+ .filter(Boolean);
+ expect(lines).toHaveLength(3);
+ },
+ INTEGRATION_TIMEOUT_MS,
+ );
+
+ itWithToken(
+ "runs a single-agent sonnet sample with the real runtime",
+ async () => {
+ const stdout = await runIntegrationSample(sonnetSamplePath, "midnight train through fog");
+ const result = JSON.parse(stdout) as { haiku: string };
+ expect(typeof result.haiku).toBe("string");
+ const lines = result.haiku
+ .split(/\r?\n/g)
+ .map((line) => line.trim())
+ .filter(Boolean);
+ expect(lines).toHaveLength(3);
+ },
+ INTEGRATION_TIMEOUT_MS,
+ );
+
+ itWithToken(
+ "runs a complex sonnet sample with tools, addons, intents, and subagent wiring",
+ async () => {
+ const stdout = await runIntegrationSample(
+ complexSamplePath,
+ JSON.stringify({
+ topic: "ship a stable release process",
+ audience: "maintainers",
+ }),
+ );
+ const result = JSON.parse(stdout) as {
+ headline: string;
+ checklist: string[];
+ riskLevel: "low" | "medium" | "high";
+ nextActions: Array<{ owner: string; action: string }>;
+ contextDigest: {
+ repository: string;
+ usedFeatures: string[];
+ toolHint: string;
+ };
+ };
+
+ expect(typeof result.headline).toBe("string");
+ expect(result.headline.length).toBeGreaterThan(0);
+ expect(Array.isArray(result.checklist)).toBe(true);
+ expect(result.checklist.length).toBeGreaterThan(0);
+ expect(["low", "medium", "high"]).toContain(result.riskLevel);
+ expect(Array.isArray(result.nextActions)).toBe(true);
+ expect(result.nextActions.length).toBeGreaterThan(0);
+ expect(typeof result.contextDigest.repository).toBe("string");
+ expect(result.contextDigest.repository.length).toBeGreaterThan(0);
+ expect(Array.isArray(result.contextDigest.usedFeatures)).toBe(true);
+ expect(result.contextDigest.usedFeatures.length).toBeGreaterThanOrEqual(5);
+ expect(typeof result.contextDigest.toolHint).toBe("string");
+ expect(result.contextDigest.toolHint.length).toBeGreaterThan(0);
+ },
+ INTEGRATION_TIMEOUT_MS,
+ );
+});
diff --git a/scripts/run-sample.test.ts b/scripts/run-sample.test.ts
new file mode 100644
index 0000000..a5334f1
--- /dev/null
+++ b/scripts/run-sample.test.ts
@@ -0,0 +1,306 @@
+/**
+ * Run a rig sample with a stub Copilot SDK client that returns shape-conforming output.
+ * Usage: npx vitest run scripts/run-sample.test.ts -- --sample 02
+ * or: npm run sample -- 02
+ */
+import { describe, it, expect, beforeEach, vi } from "vitest";
+import { readFileSync, readdirSync } from "fs";
+import { mkdtemp, rm, writeFile } from "fs/promises";
+import { tmpdir } from "os";
+import { resolve } from "path";
+import { Readable, Writable } from "stream";
+import { execFile } from "child_process";
+import { promisify } from "util";
+import { runLauncherCli } from "rig";
+
+const execFileAsync = promisify(execFile);
+
+const mocks = vi.hoisted(() => {
+ const approveAll = vi.fn();
+ let sendAndWaitImpl: (request: { prompt: string }) => unknown | Promise = async () => ({ text: "stub response" });
+ const disconnectSession = vi.fn(async () => {});
+ const stopClient = vi.fn(async () => []);
+ const createSession = vi.fn(async () => ({
+ sendAndWait: async (request: { prompt: string }) => {
+ const response = await sendAndWaitImpl(request);
+ return JSON.stringify(response);
+ },
+ disconnect: disconnectSession,
+ }));
+ const forUri = vi.fn(() => ({ kind: "uri", url: "localhost:7777" }));
+ const forStdio = vi.fn(() => ({ kind: "stdio" }));
+ const CopilotClient = function () {
+ return { createSession, stop: stopClient };
+ };
+ const setSendAndWaitImpl = (impl: (request: { prompt: string }) => unknown | Promise) => {
+ sendAndWaitImpl = impl;
+ };
+ return { approveAll, createSession, disconnectSession, stopClient, forUri, forStdio, CopilotClient, setSendAndWaitImpl };
+});
+
+vi.mock("@github/copilot-sdk", () => ({
+ approveAll: mocks.approveAll,
+ CopilotClient: mocks.CopilotClient,
+ RuntimeConnection: { forUri: mocks.forUri, forStdio: mocks.forStdio },
+}));
+
+function generateOutput(prompt: string): unknown {
+ const match = prompt.match(/([\s\S]*?)<\/output_schema>/);
+ if (!match) return { text: "stub response" };
+ const schema = match[1].trim();
+ try {
+ return parseJsonSchema(JSON.parse(schema));
+ } catch (error) {
+ if (!(error instanceof SyntaxError)) throw error;
+ // Backward compatibility with historical schema text format.
+ }
+ return parseTypeText(schema);
+}
+
+const ADDITIONAL_PROPERTIES_STUB_KEY_PREFIX = "example";
+
+function nextAdditionalPropertiesStubKey(result: Record): string {
+ let key = ADDITIONAL_PROPERTIES_STUB_KEY_PREFIX;
+ let suffix = 2;
+ while (result[key] !== undefined) {
+ key = `${ADDITIONAL_PROPERTIES_STUB_KEY_PREFIX}${suffix}`;
+ suffix += 1;
+ }
+ return key;
+}
+
+function parseJsonSchema(schema: unknown): unknown {
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return "stub";
+ const node = schema as Record;
+ if (Array.isArray(node["enum"]) && node["enum"].length > 0) return node["enum"][0];
+ if (Array.isArray(node["oneOf"]) && node["oneOf"].length > 0) return parseJsonSchema(node["oneOf"][0]);
+ if (Array.isArray(node["anyOf"]) && node["anyOf"].length > 0) return parseJsonSchema(node["anyOf"][0]);
+
+ switch (node["type"]) {
+ case "string":
+ return "stub";
+ case "number":
+ case "integer":
+ return 0;
+ case "boolean":
+ return true;
+ case "array":
+ return [];
+ case "object": {
+ const properties = node["properties"] && typeof node["properties"] === "object" && !Array.isArray(node["properties"])
+ ? node["properties"] as Record
+ : {};
+ const result: Record = {};
+ for (const [key, value] of Object.entries(properties)) {
+ result[key] = parseJsonSchema(value);
+ }
+ if (node["additionalProperties"]) {
+ const fallbackKey = nextAdditionalPropertiesStubKey(result);
+ result[fallbackKey] = parseJsonSchema(node["additionalProperties"]);
+ }
+ return result;
+ }
+ default:
+ if (node["properties"] && typeof node["properties"] === "object" && !Array.isArray(node["properties"])) {
+ const result: Record = {};
+ for (const [key, value] of Object.entries(node["properties"] as Record)) {
+ result[key] = parseJsonSchema(value);
+ }
+ return result;
+ }
+ return "stub";
+ }
+}
+
+function parseTypeText(text: string): unknown {
+ text = text.trim();
+
+ // Union/enum: "value1" | "value2" | ...
+ if (text.includes("|") && !text.startsWith("{")) {
+ const parts = text.split("|").map((s) => s.trim());
+ // Pick first non-null
+ for (const p of parts) {
+ if (p === "null") continue;
+ if (p.startsWith('"') && p.endsWith('"')) return p.slice(1, -1);
+ if (p === "string") return "stub";
+ if (p === "number") return 0;
+ if (p === "boolean") return true;
+ return p;
+ }
+ return null;
+ }
+
+ // Array: type[]
+ if (text.endsWith("[]")) return [];
+
+ // Primitives
+ if (text === "string") return "stub";
+ if (text === "number") return 0;
+ if (text === "boolean") return true;
+ if (text === "unknown") return null;
+
+ // Object: { ... }
+ if (text.startsWith("{")) {
+ const inner = text.slice(1, text.lastIndexOf("}")).trim();
+ if (!inner) return {};
+ const result: Record = {};
+ const fields = splitFields(inner);
+ for (const field of fields) {
+ // [key: string]: type (wildcard/record)
+ const wildcardMatch = field.match(/^\[key:\s*string\]\s*:\s*([\s\S]+?);?\s*$/);
+ if (wildcardMatch) {
+ result["example"] = parseTypeText(wildcardMatch[1].replace(/;\s*$/, "").trim());
+ continue;
+ }
+ // key?: type; or key: type; (type may be multi-line for nested objects)
+ const fieldMatch = field.match(/^(\w+)(\?)?\s*:\s*([\s\S]+?);?\s*$/);
+ if (fieldMatch) {
+ const [, key, , type] = fieldMatch;
+ result[key] = parseTypeText(type.replace(/;\s*$/, "").trim());
+ }
+ }
+ if (Object.keys(result).length === 0) return { text: "stub" };
+ return result;
+ }
+
+ // Quoted literal
+ if (text.startsWith('"') && text.endsWith('"')) return text.slice(1, -1);
+ // Numeric literal
+ if (/^\d+$/.test(text)) return Number(text);
+ // Boolean literal
+ if (text === "true") return true;
+ if (text === "false") return false;
+
+ return "stub";
+}
+
+function splitFields(inner: string): string[] {
+ const fields: string[] = [];
+ let depth = 0;
+ let current = "";
+ for (const line of inner.split("\n")) {
+ const trimmed = line.trim();
+ if (!trimmed) continue;
+ current += (current ? "\n" : "") + trimmed;
+ depth += (trimmed.match(/\{/g) || []).length;
+ depth -= (trimmed.match(/\}/g) || []).length;
+ // A field is complete when we're back to depth 0 and line ends with ;
+ if (depth <= 0 && (trimmed.endsWith(";") || trimmed.endsWith("}"))) {
+ fields.push(current);
+ current = "";
+ depth = 0;
+ }
+ }
+ if (current) fields.push(current);
+ return fields;
+}
+
+// Determine which samples to run
+const sampleDir = resolve(__dirname, "../src/samples");
+const allFiles = readdirSync(sampleDir)
+ .filter((f) => f.endsWith(".ts"))
+ .sort();
+const markdownDir = resolve(__dirname, "../skills/rig/samples");
+const allMarkdownFiles = readdirSync(markdownDir)
+ .filter((f) => f.endsWith(".md"))
+ .sort();
+
+const filter = process.env["RIG_SAMPLE"];
+const targets = filter
+ ? allFiles.filter((f) => f.includes(filter))
+ : allFiles;
+const markdownTargets = filter
+ ? allMarkdownFiles.filter((f) => f.includes(filter))
+ : allMarkdownFiles;
+
+function extractRigCode(markdown: string): string {
+ const match = markdown.match(/```rig\n([\s\S]*?)\n```/);
+ if (!match) throw new Error("Expected sample markdown to contain a ```rig code fence.");
+ return match[1];
+}
+
+function withTypecheckModel(code: string): string {
+ return code.replace(/model:\s*"[^"]+"/g, 'model: "typecheck"');
+}
+
+beforeEach(() => {
+ mocks.createSession.mockClear();
+ mocks.setSendAndWaitImpl(async ({ prompt }) => generateOutput(prompt));
+});
+
+describe("samples", () => {
+ for (const file of targets) {
+ it(file, async () => {
+ // Dynamically import the sample — vitest resolves "rig" via alias
+ // Samples use top-level await so the import itself runs them
+ const start = performance.now();
+ try {
+ await import(`../src/samples/${file}`);
+ } catch (e: any) {
+ // Timeout/abort errors are expected for samples that test those features
+ if (e?.message?.includes("Timed out") || e?.name === "AbortError") {
+ // expected
+ } else {
+ throw e;
+ }
+ }
+ const elapsed = performance.now() - start;
+ expect(elapsed).toBeLessThan(5000);
+ });
+ }
+});
+
+describe("skill markdown samples", () => {
+ for (const file of markdownTargets) {
+ it(file, async () => {
+ const code = extractRigCode(readFileSync(resolve(markdownDir, file), "utf8"));
+ const runnableCode = withTypecheckModel(code);
+ expect(code.split("\n").length).toBeLessThanOrEqual(30);
+ expect(code).toContain("export default");
+ expect(code).toContain("// Agent role:");
+ expect(code).not.toContain("console.log");
+ expect((code.match(/^import .* from "rig";$/gm) ?? [])).toHaveLength(1);
+ expect(code).not.toMatch(/^await\s+\w+\(/m);
+
+ const stdin = Readable.from([runnableCode]);
+ const output: string[] = [];
+ const stdout = new Writable({
+ write(chunk, _encoding, callback) {
+ output.push(chunk.toString());
+ callback();
+ },
+ });
+
+ const start = performance.now();
+ await runLauncherCli([], {}, { stdin, stdout });
+ const elapsed = performance.now() - start;
+ expect(elapsed).toBeLessThan(5000);
+ expect(output.join("")).not.toBe("");
+ });
+ }
+});
+
+describe("skill markdown samples typecheck", () => {
+ it("typechecks extracted rig programs with npx tsc", async () => {
+ const typecheckDir = await mkdtemp(resolve(tmpdir(), "rig-sample-typecheck-"));
+ try {
+ for (const file of markdownTargets) {
+ const markdown = readFileSync(resolve(markdownDir, file), "utf8");
+ const code = withTypecheckModel(extractRigCode(markdown));
+ const tsFile = resolve(typecheckDir, file.replace(/\.md$/, ".ts"));
+ await writeFile(tsFile, `${code}\n`, "utf8");
+ }
+
+ await execFileAsync(
+ "npx",
+ ["--yes", "--package", "typescript@5.9.3", "--", "tsc", "--noEmit", "--pretty", "false"],
+ {
+ cwd: resolve(__dirname, ".."),
+ env: { ...process.env, npm_config_ignore_scripts: "true" },
+ },
+ );
+ } finally {
+ await rm(typecheckDir, { recursive: true, force: true });
+ }
+ });
+});
diff --git a/skills/rig/SKILL.md b/skills/rig/SKILL.md
new file mode 100644
index 0000000..7be8b3e
--- /dev/null
+++ b/skills/rig/SKILL.md
@@ -0,0 +1,376 @@
+# rig
+
+Minimal TypeScript agent harness skill for structured agent calls inside sandboxed agentic workflows, intended for embedding in markdown with `rig` code fences.
+
+## Install
+
+Install the latest GitHub release directly:
+
+```bash
+npm install github:pelikhan/rig#v0.0.8
+```
+
+Or clone the skill for Copilot coding agent:
+
+```bash
+gh skills clone pelikhan/rig
+```
+
+## Preferred imports
+
+```ts
+import { agent, p, s } from "rig";
+```
+
+## Recommended default pattern
+
+Prefer this shape when generating a new rig program:
+
+```ts
+import { agent, p, s } from "rig";
+
+// Agent role: review the diff and return only the declared output.
+const reviewDiff = agent({
+ model: "mini",
+ instructions: "Review the diff and return only the declared output.",
+ output: s.object({
+ summary: s.string,
+ risk: s.enum("low", "medium", "high"),
+ findings: s.array(s.object({
+ file: s.string,
+ message: s.string,
+ line: s.optional(s.number),
+ })),
+ }),
+});
+
+export default reviewDiff;
+```
+
+## Fast generation checklist
+
+Use this checklist before finalizing generated code:
+
+1. Use a single `import { ... } from "rig"` statement.
+2. Use `agent({ ... })`; include `name` when it helps differentiate agents, keep `instructions` and `output` explicit, and include `input` when the scenario needs it.
+3. Define input/output with `s.object(...)` and explicit `s.*` helpers.
+4. Keep output schema strict (enums/literals for constrained values).
+5. Add a `// Agent role: ...` comment above each agent declaration.
+6. Set `model` explicitly to `"large"`, `"mini"`, or `"nano"`.
+7. Prefer `${p.read(...)}` / `${p.bash(...)}` inside `p\`\`` templates when the context source is already known; add input fields only for true caller-provided data.
+8. Put stable defaults in spec; register addons in spec or with `agent.use(...)`.
+9. Add `agents` only when required by the scenario.
+10. Avoid `console.log(...)` in snippets.
+11. For inline markdown skill mode, export exactly one default root agent with no input and do not call it directly.
+12. Assume Node.js 24 runtime for operational examples and generated snippets.
+13. For bash-like operations from TypeScript, prefer `google/zx` (`import { $ } from "zx"`).
+14. Prefer Node.js native APIs (for example built-in `fetch` and native glob support) over extra helper dependencies.
+
+## Canonical construction order
+
+Use this order to reduce syntax drift:
+
+1. Core agent shape: `agent({ name, instructions, input, output })`.
+2. Explicit typed schemas with `s.object(...)` and `s.*`.
+3. Shell/file context with `p\`\`` and `${p.*}` before adding extra input plumbing.
+4. Advanced spec fields (`agents`) when scenario needs them.
+5. Invocation overrides (`model`, `timeout`, `maxTurns`, `signal`) at call time.
+
+## `agent(spec)`
+
+Declare a structured agent.
+
+### Spec fields
+
+| Field | Purpose |
+|-------|---------|
+| `name` | Agent name used in the prompt |
+| `instructions` | Prompt instructions as a plain string or a ``p`...` `` prompt builder |
+| `input` | Input schema |
+| `output` | Output schema |
+| `model` | Default model name; examples should use `"large"`, `"mini"`, or `"nano"` |
+| `maxTurns` | Retry budget for invalid JSON or invalid output |
+| `addons` | Per-turn addons for steering, validation, and retry customization |
+| `agents` | Optional named subagents exposed to the harness |
+
+Use `agent({ name, ... })` as the only agent declaration form. `name` is optional; when omitted rig normalizes it to `"agent"`.
+
+## Agent behavior defaults
+
+| Setting | Default |
+|---------|---------|
+| Model | `gpt-4.1` |
+| Max turns | `4` |
+| Addons | none |
+
+Override per call with `model`, `maxTurns`, `timeout`, and `signal`. Put stable defaults in the agent spec; use call-time options for per-run changes.
+
+## Schemas
+
+Use `s.*` helpers for input and output schemas.
+These `s.*` declarations must stay JSON Schema-compatible and serialize directly as JSON Schema because rig renders prompt schema blocks as JSON Schema.
+
+```ts
+input: s.object({
+ title: s.string,
+ severity: s.enum("low", "medium", "high"),
+})
+```
+
+Use explicit schemas in docs and generated samples.
+
+## `s` schema helpers
+
+```ts
+s.string
+s.string("description")
+s.number
+s.boolean
+s.unknown
+s.array(item)
+s.array(item, "description")
+s.object(fields)
+s.object(fields, "description")
+s.record(value)
+s.record(value, "description")
+s.enum(...values)
+s.enum(values, "description")
+s.optional(shape)
+s.optional(shape, "description")
+```
+
+Common examples:
+
+```ts
+s.enum("bug", "feature", "question")
+s.optional(s.number)
+s.record(s.string)
+```
+
+## Tools
+
+Register custom tools with `defineTool` using the same shape as `@github/copilot-sdk`. Use `s.*` schemas for `parameters`. Rig defaults tools to `skipPermission: true`.
+
+```ts
+import { agent, defineTool, s } from "rig";
+
+const lookupIssue = defineTool("lookup_issue", {
+ description: "Look up an issue by id.",
+ parameters: s.object({
+ issue: s.string,
+ }),
+ handler: async ({ issue }) => `Issue ${issue}`,
+});
+
+// Agent role: triage an issue using the lookup tool.
+const triage = agent({
+ model: "mini",
+ instructions: "Use lookup_issue before answering.",
+ tools: [lookupIssue],
+});
+```
+
+## Prompt helpers
+
+`p` is both the prompt template tag and the prompt-intent helper namespace.
+These helpers are declarative placeholders, not direct shell execution in the core harness.
+Prefer template expressions when the context source is already known.
+Prefer `p.read("path")` over `p.bash("cat path")`, and keep large context in files instead of building in-memory strings just to feed an agent.
+Rig assumes the surrounding workflow already provides the sandbox and protections it needs, so prompt intents for shell/file actions should execute directly without extra permission prompts.
+
+```ts
+p.bash("git diff -- .")
+p.bash("npm test")
+p.read("README.md")
+p.write("README.md", "# Hello\n")
+```
+
+Use `p.*` helpers:
+
+- in input values
+- inside `p\`\`` instruction templates, preferably as the default pattern
+
+```ts
+const prompt = p`Review the repository status using ${p.bash("git status --short")}.`;
+```
+
+Only introduce `input` fields for data the caller truly supplies at runtime. Do not require inputs just to thread known file or shell context into the prompt.
+
+## `p` as a prompt builder for instructions
+
+``p`...` `` can also be used in `instructions` when you want to embed prompt intents or inline context directly in the agent instructions.
+
+```ts
+import { agent, p, s } from "rig";
+
+const reviewAgent = agent({
+ instructions: p`Review the repository using ${p.bash("git status --short")} and summarize changes.`,
+ output: s.object({ summary: s.string }),
+});
+```
+
+- ``p`...` `` accepts `${p.bash(...)}`, `${p.read(...)}`, and `${p.write(...)}` expressions.
+- Nested `PromptBuilder` values used as interpolations are inlined as plain text.
+- The rendered `PromptBuilder` replaces the instructions string when the agent prompt is assembled.
+
+## Call-time options
+
+Pass overrides when calling an agent:
+
+```ts
+const controller = new AbortController();
+
+const result = await myAgent(input, {
+ model: "mini",
+ timeout: 30_000,
+ maxTurns: 2,
+ signal: controller.signal,
+});
+```
+
+Use call-time options for per-run changes. Use addons for stable defaults (for example `timeout({ timeout: ... })`).
+
+## Subagents
+
+Expose subagents with `agents`:
+
+```ts
+// Agent role: extract the most important changes from the diff.
+const summarizeDiff = agent({
+ model: "mini",
+});
+
+// Agent role: review the diff using the provided subagent when helpful.
+const reviewer = agent({
+ model: "mini",
+ output: s.object({
+ summary: s.string,
+ issues: s.array(s.string),
+ }),
+ agents: { summarizeDiff },
+ instructions: "Review the diff. You may use the provided subagent conceptually.",
+});
+```
+
+When delegating task resolution, keep each subagent narrow and explicit (for example: `analyzeTask`, `draftRigProgram`, `verifySchema`) and make the root agent instructions require combining their outputs into one final response.
+
+## Task harness pattern for rig markdown
+
+When the task asks for a runnable markdown example, require exactly one fenced ````rig` block that is valid inline harness input:
+
+- include `import { ... } from "rig"` (or rely on inline injection intentionally)
+- define one default-exported no-input root agent
+- avoid calling the root agent directly in the snippet
+- keep the block aligned with this skill's construction order and checklist
+
+## Repair and retries
+
+Rig starts with no addons by default. Opt into retry behavior with `repair` from `rig/addons`.
+
+```ts
+import { repair } from "rig/addons";
+
+// Agent role: repair invalid output and return a stable summary.
+const summarize = agent({
+ model: "mini",
+ maxTurns: 3,
+ addons: repair,
+});
+```
+
+Use addons to steer retry prompts when needed (for example `steering()` from `rig/addons`).
+Use `oncePerSession()` from `rig/addons` when you need to register with the Copilot session once per call instead of checking `context.turn === 1`.
+
+The addon `context` object contains: `prompt`, `response`, `turn`, `maxTurns`, `signal`, `output`, `nextPrompt`, `error`, `completed`, and `session` (direct Copilot SDK session access).
+
+## Running programs
+
+Treat fenced `rig` code blocks in markdown as runnable rig programs.
+Run them by extracting the fence content and piping it into `node skills/rig/rig.ts`.
+Inline programs run a default-exported root program with no required external input and write stdout. Export either an agent, a string, or a prompt builder value. If `export default` is omitted, the harness defaults to the first `const/let/var name = agent(...)` assignment:
+
+```bash
+cat <<'RIG' | node skills/rig/rig.ts
+// Agent role: summarize this repository in one sentence.
+export default "Summarize this repository in one sentence.";
+RIG
+```
+
+`import { agent, p, s } from "rig"` is optional in inline mode; the harness injects it if omitted.
+
+Inline mode accepts root agents that either omit `input`, use `input: s.object({})`, or rely on the default `input: s.string` (which is invoked with `""`).
+
+The harness also supports program-file mode. Export the root program as the default export and pass input on stdin:
+
+```bash
+echo "Review this diff" | node skills/rig/rig.ts src/program.ts
+```
+
+Pass `--server` to have the harness start the Copilot server automatically before running:
+
+```bash
+echo "Review this diff" | node skills/rig/rig.ts src/program.ts --server
+```
+
+Pass `--typecheck` to typecheck the rig program before execution:
+
+```bash
+cat <<'RIG' | node skills/rig/rig.ts --typecheck
+import { p } from "rig";
+export default p`Summarize this repository and include highlights from ${p.read("README.md")}.`;
+RIG
+```
+
+Program-file mode also supports `--typecheck`:
+
+```bash
+echo "Review this diff" | node skills/rig/rig.ts src/program.ts --typecheck
+```
+
+For program-file mode stdin coercion:
+- if root input schema is `string`, stdin is passed as raw text
+- if root input schema is an object containing `text`, stdin is passed as `{ text: "" }`
+- otherwise stdin must be valid JSON for the declared input schema
+
+## Copilot SDK runtime
+
+`rig` is specialized for Copilot SDK sessions and no longer exposes a custom engine mount API.
+By default it connects over HTTP using `COPILOT_SDK_URI`, then `localhost:7777`.
+Use `--server` at launch time when you want the harness to start the Copilot server via stdio.
+
+## Patterns to prefer
+
+- Prefer `s.object(...)` for important examples. Omit schemas entirely when the default free-form string is enough.
+- Keep outputs small, typed, and explicit.
+- Use `s.enum(...)` when exact values matter.
+- Prefer `p.*` inside `p\`\`` templates; fall back to inputs only for real caller-provided data.
+- Prefer `p.read(...)` for existing files instead of shelling out through `cat`.
+- Assume Node.js 24 runtime for operational code.
+- Prefer `google/zx` for shell-style automation in TypeScript examples.
+- Prefer Node.js native APIs (including built-in `fetch` and native glob support) before adding dependencies.
+- Put durable defaults in the agent spec; register addons in spec or with `agent.use(...)`.
+- Use `steering()` from `rig/addons` when you want the builtin last-retry warning addon; it is opt-in.
+- Introduce `agents` only when the scenario needs them.
+
+## Patterns to avoid
+
+- When a free-form string is enough, omit `input`/`output` and use the default `s.string` schemas.
+- Do not wrap a single string field in an input object just to carry text.
+- Do not import prompt helpers from anywhere except `rig`.
+- Do not require `input` fields just to pass `p.read(...)` / `p.bash(...)` context into instructions.
+- Do not leave outputs as unstructured prose when a schema would help.
+- Do not invent alternate schema syntaxes when explicit `s.*` is available.
+- Do not replace file reads with `cat`-style shell commands or large in-memory strings when a file path already exists.
+- Do not add third-party fetch or glob helpers when Node.js 24 native APIs already cover the requirement.
+- Do not put call-time overrides (`model`, `timeout`, `maxTurns`, `signal`) into unrelated config objects.
+
+## API direction
+
+Use only the current API:
+
+- `agent({ name, ... })`
+- `p.*` and `p\`...\`` from `rig`
+- `s.*` for explicit schema helpers
+- `oncePerSession` / `repair` / `steering` / `timeout` from `rig/addons` for optional addons
+
+Do not add deprecated hooks or compatibility layers.
diff --git a/skills/rig/addons.ts b/skills/rig/addons.ts
new file mode 100644
index 0000000..cdda1a4
--- /dev/null
+++ b/skills/rig/addons.ts
@@ -0,0 +1,88 @@
+import { analyzeResponse, defaultRepairPrompt } from "./rig.ts";
+import type { AgentAddon, AgentAddonContext, CopilotSession } from "./rig.ts";
+
+const DEFAULT_STEERING_WARNING = "You are running out of turns. This is your final attempt before reaching the turn limit. Please correct your output now.";
+
+export type SteeringOptions = {
+ message?: string;
+};
+
+export type TimeoutOptions = {
+ timeout: number;
+};
+
+export type SessionRegistration = (
+ session: CopilotSession,
+ context: AgentAddonContext,
+) => void | Promise;
+
+export function steering(options: SteeringOptions = {}): AgentAddon {
+ const message = options.message ?? DEFAULT_STEERING_WARNING;
+ return async (context, next) => {
+ await next();
+ if (context.nextPrompt && context.turn + 1 === context.maxTurns) {
+ context.nextPrompt = `${context.nextPrompt}\n${message}`;
+ }
+ };
+}
+
+export const repair: AgentAddon = async (context, next) => {
+ await next();
+ if (context.completed || context.error !== undefined || context.nextPrompt !== undefined) {
+ return;
+ }
+ if (context.response === undefined) {
+ return;
+ }
+ const analysis = analyzeResponse(context.response, context.outputSchema, context.spec.name, context.turn);
+ if (analysis.ok) {
+ context.completed = true;
+ context.output = analysis.output;
+ return;
+ }
+ if (context.turn >= context.maxTurns) {
+ context.error = analysis.error;
+ return;
+ }
+ context.nextPrompt = defaultRepairPrompt(context.spec, analysis.error);
+};
+
+export function timeout(options: TimeoutOptions): AgentAddon {
+ return async (context, next) => {
+ context.signal = timeoutSignal(context.signal, options.timeout);
+ await next();
+ };
+}
+
+export function oncePerSession(register: SessionRegistration): AgentAddon {
+ const seen = new WeakSet();
+ return async (context, next) => {
+ if (!seen.has(context.session)) {
+ await register(context.session, context);
+ seen.add(context.session);
+ }
+ await next();
+ };
+}
+
+function timeoutSignal(parent?: AbortSignal, timeoutMs?: number): AbortSignal | undefined {
+ if (!timeoutMs) {
+ return parent;
+ }
+ const controller = new AbortController();
+ const onAbort = () => controller.abort(parent?.reason);
+ parent?.addEventListener("abort", onAbort, { once: true });
+ const timer = setTimeout(
+ () => controller.abort(new Error(`Timed out after ${timeoutMs}ms`)),
+ timeoutMs,
+ );
+ controller.signal.addEventListener("abort", () => clearTimeout(timer), { once: true });
+ return controller.signal;
+}
+
+export const addons = {
+ oncePerSession,
+ timeout,
+ repair,
+ steering,
+};
diff --git a/skills/rig/rig.ts b/skills/rig/rig.ts
new file mode 100644
index 0000000..38ccda8
--- /dev/null
+++ b/skills/rig/rig.ts
@@ -0,0 +1,1586 @@
+import { basename, dirname, isAbsolute, resolve } from "node:path";
+import { fileURLToPath, pathToFileURL } from "node:url";
+import { access, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
+import { execFile } from "node:child_process";
+import { AsyncLocalStorage } from "node:async_hooks";
+import { promisify } from "node:util";
+import { CopilotClient, RuntimeConnection, approveAll, defineTool as sdkDefineTool } from "@github/copilot-sdk";
+import type {
+ CopilotClientOptions,
+ SystemMessageConfig,
+ Tool as CopilotTool,
+ ToolHandler,
+ ZodSchema,
+} from "@github/copilot-sdk";
+
+export type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
+export type ValidationResult = { ok: true } | { ok: false; error: string };
+
+export type StringSchema = { type: "string"; description?: string };
+export type NumberSchema = { type: "number"; description?: string };
+export type BooleanSchema = { type: "boolean"; description?: string };
+export type UnknownSchema = { description?: string };
+export type ArraySchema- = { type: "array"; items: Item; description?: string };
+export type ObjectSchema = Record> = {
+ type: "object";
+ properties: Fields;
+ description?: string;
+};
+export type RecordSchema = { type: "object"; additionalProperties: Value; description?: string };
+export type EnumSchema = { enum: Values; description?: string };
+const OPTIONAL_SYMBOL: unique symbol = Symbol("rig.optional");
+type OptionalMarker = { readonly [OPTIONAL_SYMBOL]: true };
+type UnwrapOptional = Omit;
+export type OptionalSchema = Inner & OptionalMarker;
+
+export type Schema =
+ | StringSchema
+ | NumberSchema
+ | BooleanSchema
+ | UnknownSchema
+ | ArraySchema
+ | ObjectSchema
+ | RecordSchema
+ | EnumSchema
+ | OptionalSchema;
+
+type SchemaHelperFactory = T & ((description?: string) => T);
+
+const SCHEMA_SYMBOL: unique symbol = Symbol("rig.schema");
+
+function markAsSchema(obj: T): T {
+ Object.defineProperty(obj, SCHEMA_SYMBOL, { value: true, enumerable: false, writable: false, configurable: false });
+ Object.defineProperty(obj, "toJSON", {
+ value: () => serializeSchema(obj as unknown as Schema),
+ enumerable: false,
+ writable: false,
+ configurable: false,
+ });
+ return obj;
+}
+
+function cloneSchema(schema: Inner, description?: string): Inner {
+ const cloned = { ...(schema as Record) } as Inner;
+ if (description !== undefined) {
+ Object.assign(cloned as object, { description });
+ }
+ return markAsSchema(cloned as unknown as object) as Inner;
+}
+
+function markAsOptional(schema: Inner): OptionalSchema {
+ Object.defineProperty(schema, OPTIONAL_SYMBOL, { value: true, enumerable: false, writable: false, configurable: false });
+ return schema as OptionalSchema;
+}
+
+function isOptionalSchema(schema: Schema): schema is OptionalSchema {
+ return OPTIONAL_SYMBOL in schema;
+}
+
+function createTypedPrimitiveSchema(type: T["type"]): SchemaHelperFactory {
+ const base = markAsSchema({ type } as T);
+ const factory = Object.assign(
+ markAsSchema(((description?: string) => (description === undefined ? base : markAsSchema({ type, description } as T))) as SchemaHelperFactory),
+ base,
+ );
+ return factory;
+}
+
+function createUnknownSchema(): SchemaHelperFactory {
+ const base: UnknownSchema = markAsSchema({});
+ const factory = Object.assign(
+ markAsSchema(((description?: string) => (description === undefined ? base : markAsSchema({ description }))) as SchemaHelperFactory),
+ base,
+ );
+ return factory;
+}
+
+type EnumSchemaFactory = {
+ (...values: Values): EnumSchema;
+ (values: Values, description: string): EnumSchema;
+};
+
+const createEnumSchema: EnumSchemaFactory = (...args: unknown[]) => {
+ const valuesOrTuple = args as readonly Json[];
+ if (
+ valuesOrTuple.length === 2
+ && Array.isArray(valuesOrTuple[0])
+ && typeof valuesOrTuple[1] === "string"
+ ) {
+ const enumValues = valuesOrTuple[0] as readonly Json[];
+ const description = valuesOrTuple[1] as string;
+ return markAsSchema({ enum: enumValues, description });
+ }
+ const enumValues = valuesOrTuple as readonly Json[];
+ return markAsSchema({ enum: enumValues });
+};
+
+export type Simplify = { [K in keyof T]: T[K] } & {};
+
+export type AgentInputValue =
+ T extends readonly (infer Item)[] ? PromptIntent | PromptBuilder | AgentInputValue
- [] :
+ T extends object ? PromptIntent | PromptBuilder | { [K in keyof T]: AgentInputValue } :
+ T | PromptIntent | PromptBuilder;
+
+export type InferSchema =
+ T extends OptionalMarker ? InferSchema> | undefined :
+ T extends { type: "string" } ? string :
+ T extends { type: "number" } ? number :
+ T extends { type: "boolean" } ? boolean :
+ T extends { enum: infer Values extends readonly unknown[] } ? Values[number] :
+ T extends { type: "array"; items: infer Item } ? InferSchema
- [] :
+ T extends { type: "object"; properties: infer Fields extends Record } ? Simplify<
+ & { [K in keyof Fields as Fields[K] extends OptionalMarker ? never : K]: InferSchema }
+ & { [K in keyof Fields as Fields[K] extends OptionalMarker ? K : never]?: InferSchema> }
+ > :
+ T extends { type: "object"; additionalProperties: infer Value } ? Record> :
+ unknown;
+
+export const s = {
+ string: createTypedPrimitiveSchema("string"),
+ number: createTypedPrimitiveSchema("number"),
+ boolean: createTypedPrimitiveSchema("boolean"),
+ unknown: createUnknownSchema(),
+ array
- (items: Item, description?: string): ArraySchema
- {
+ return description === undefined ? markAsSchema({ type: "array", items }) : markAsSchema({ type: "array", items, description });
+ },
+ object>(properties: Fields, description?: string): ObjectSchema {
+ return description === undefined ? markAsSchema({ type: "object", properties }) : markAsSchema({ type: "object", properties, description });
+ },
+ record(additionalProperties: Value, description?: string): RecordSchema {
+ return description === undefined ? markAsSchema({ type: "object", additionalProperties }) : markAsSchema({ type: "object", additionalProperties, description });
+ },
+ enum: createEnumSchema,
+ optional(schema: Inner, description?: string): OptionalSchema {
+ return markAsOptional(cloneSchema(schema, description));
+ },
+ toJsonSchema,
+};
+
+export type JsonSchemaObject = { [key: string]: unknown };
+
+export function toJsonSchema(schema: Schema): JsonSchemaObject {
+ return serializeSchema(schema);
+}
+
+function serializeSchema(schema: Schema): JsonSchemaObject {
+ const { description } = schema;
+ const withDescription = (obj: JsonSchemaObject): JsonSchemaObject =>
+ description === undefined ? obj : { ...obj, description };
+ if ("enum" in schema) {
+ return withDescription({ enum: schema.enum });
+ }
+ if ("items" in schema) {
+ return withDescription({ type: "array", items: serializeSchema(schema.items) });
+ }
+ if ("additionalProperties" in schema) {
+ return withDescription({ type: "object", additionalProperties: serializeSchema(schema.additionalProperties) });
+ }
+ if ("properties" in schema) {
+ const properties: Record = {};
+ const required: string[] = [];
+ for (const [key, field] of Object.entries(schema.properties) as [string, Schema][]) {
+ properties[key] = serializeSchema(field);
+ if (!isOptionalSchema(field)) {
+ required.push(key);
+ }
+ }
+ const obj: JsonSchemaObject = { type: "object", properties };
+ if (required.length > 0) {
+ obj["required"] = required;
+ }
+ return withDescription(obj);
+ }
+ if ("type" in schema) {
+ return withDescription({ type: schema.type });
+ }
+ return withDescription({});
+}
+
+const defaultStringSchema = s.string;
+const defaultName = "agent";
+
+export type CopilotEngineOptions = Omit & {
+ connection?: CopilotClientOptions["connection"];
+ server?: boolean;
+};
+
+function resolveDefaultCopilotUri(): string {
+ return process.env["COPILOT_SDK_URI"] ?? "localhost:7777";
+}
+
+export function copilotEngine(options: CopilotEngineOptions = {}): CopilotClient {
+ const { server, connection, ...clientOptions } = options;
+ return new CopilotClient({
+ ...clientOptions,
+ connection: connection ?? (server ? RuntimeConnection.forStdio() : RuntimeConnection.forUri(resolveDefaultCopilotUri())),
+ });
+}
+
+function jsonl(value: unknown): string {
+ try {
+ return JSON.stringify(value, (_, v) => {
+ if (typeof v === "bigint") {
+ return v.toString();
+ }
+ if (v instanceof Error) {
+ return { name: v.name, message: v.message, stack: v.stack };
+ }
+ return v;
+ });
+ } catch (error) {
+ const reason = error instanceof Error ? error.message : String(error);
+ return JSON.stringify(rigEvent("logger.error", { error: reason }));
+ }
+}
+
+function rigEvent(type: string, data?: unknown): { type: string; data?: unknown } {
+ return { type: `rig.${type}`, data };
+}
+
+function writeEvent(event: unknown): void {
+ process.stderr.write(`${jsonl(event)}\n`);
+}
+
+export type CopilotSession = Awaited>;
+export type AgentAddonContext = {
+ spec: NormalizedAgentSpec;
+ session: CopilotSession;
+ input: unknown;
+ outputSchema: Schema;
+ signal: AbortSignal | undefined;
+ turn: number;
+ maxTurns: number;
+ prompt: string;
+ response?: string;
+ completed: boolean;
+ output?: unknown;
+ nextPrompt?: string;
+ error?: unknown;
+};
+export type AgentAddon = (
+ context: AgentAddonContext,
+ next: () => Promise,
+) => void | Promise;
+export type Tool = CopilotTool;
+export type ToolParameters = Schema | ZodSchema | Record;
+export type ToolConfig = {
+ description?: string;
+ parameters?: ToolParameters;
+ handler?: ToolHandler;
+ overridesBuiltInTool?: boolean;
+ skipPermission?: boolean;
+};
+
+export function defineTool(name: string, config: ToolConfig): Tool {
+ return sdkDefineTool(name, {
+ ...normalizeToolConfig(config),
+ parameters: normalizeToolParameters(config.parameters),
+ });
+}
+
+export type AgentSpec = {
+ name?: string;
+ instructions?: string | PromptBuilder;
+ input?: Input;
+ output?: Output;
+ model?: string;
+ maxTurns?: number;
+ addons?: AgentAddon | AgentAddon[];
+ agents?: Record>;
+ systemMessage?: SystemMessageConfig;
+ tools?: Tool[];
+};
+/** Internal normalized variant with a guaranteed resolved name. */
+type NormalizedAgentSpec = AgentSpec & { name: string };
+
+export type CallOptions = {
+ signal?: AbortSignal;
+ timeout?: number;
+ model?: string;
+ maxTurns?: number;
+};
+
+export type LaunchOptions = {
+ cwd?: string;
+ startServer?: boolean;
+ typecheck?: boolean;
+};
+
+export type LauncherIo = {
+ stdin: NodeJS.ReadableStream;
+ stdout: NodeJS.WritableStream;
+};
+
+export type AgentFn = ((input: AgentInputValue, options?: CallOptions) => Promise