Infer Agent Action

September 3, 2026 ยท View on GitHub

Infer Agent Action

A GitHub Action that automatically runs the Infer CLI agent on GitHub issues. The agent can analyze issues, provide solutions, and post results as comments using various AI providers (Anthropic, OpenAI, Google, Ollama, Ollama Cloud, Groq, Moonshot and more).

License: Apache 2.0 GitHub release GitHub issues

Features

  • ๐Ÿค– Automatically trigger AI agents on GitHub issues
  • ๐Ÿ”„ Support for multiple AI providers (Anthropic Claude, OpenAI GPT, Google Gemini)
  • ๐ŸŽฏ Customizable trigger phrases
  • ๐Ÿ”€ Dynamic model selection - override the default model per-issue or per-comment
  • ๐Ÿ“ Automatic comment posting with results and progress tracking
  • ๐Ÿ”€ Automatic pull request creation when file changes are made
  • โš™๏ธ Configurable agent behavior and iteration limits
  • ๐Ÿš€ Easy setup with minimal configuration

Quick Start

Create a workflow file (e.g., .github/workflows/infer-agent.yml):

name: Infer Agent

on:
  issues:
    types:
      - opened
      - edited
  issue_comment:
    types:
      - created
  pull_request_review_comment:
    types:
      - created

jobs:
  run-agent:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v7.0.0

      - uses: inference-gateway/infer-action@main
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          model: deepseek/deepseek-v4-flash
          deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}

Usage Examples

Basic Usage with Anthropic Claude

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: anthropic/claude-opus-5
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}

Using OpenAI GPT-5

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: openai/gpt-5
    openai-api-key: ${{ secrets.OPENAI_API_KEY }}

Using Google Gemini 3

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: google/gemini-3-pro
    google-api-key: ${{ secrets.GOOGLE_API_KEY }}

Custom Trigger Phrase

By default, the action triggers on @infer. You can customize this:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    trigger-phrase: "@ai-helper"

Dynamic Model Selection

You can override the default model on a per-issue or per-comment basis using the /model parameter. This allows you to experiment with different models without changing your workflow configuration.

Usage in issue comments:

@infer /model deepseek/deepseek-v4-flash can you explain what this project does?

Usage in issue bodies:

@infer /model openai/gpt-5 please analyze this bug and suggest a fix

Supported model format:

The model parameter accepts any valid model identifier in the format provider/model-name, such as:

  • deepseek/deepseek-v4-flash
  • openai/gpt-5
  • google/gemini-3-pro
  • deepseek/deepseek-v4-flash
  • ollama_cloud/qwen3-coder:480b
  • moonshot/kimi-k2

The model specified in the workflow configuration serves as the default when no /model parameter is provided.

Choosing a model. Capable models (Sonnet, Opus, GPT-5, Gemini-3-Pro) follow the branch/commit/PR protocol best and produce clean, self-contained PRs. Flash/mini tiers (Haiku, GPT-5-mini, Gemini-3-Flash) are faster and cheaper but more often skip the git workflow, leaning on the salvage net to rescue their work. For production repos where PR quality matters, prefer a capable model; for triage, Q&A, and lightweight tasks the flash tiers are a good fit.

Review Mode

Ask the agent to review instead of change code, and it produces feedback only - the Edit, Write, and Delete tools are hard-disabled for the run, so even a weak model cannot modify files, commit, or push. It reads the diff and reports findings (including proposed changes as descriptions or fenced diffs) in a single comment.

Trigger review mode either way:

@infer /review
@infer review this PR for correctness and simplification
@infer can you review this?

A request where "review" is a noun behind a change verb still edits normally - e.g. @infer fix the review comments or @infer address the review feedback are treated as ordinary code-change tasks.

Inline Review Comments (review-inline)

By default, review results are posted as a single issue/PR comment. Set review-inline: true to post findings as a real GitHub pull request review with inline, line-anchored comments instead. The agent ends its response with a structured json:findings block; the runner parses it, creates the PR review, and strips the block from the summary comment so only the prose remains visible.

Limiting Agent Iterations

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}
    max-turns: 30

Using Specific CLI Version

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    version: v0.112.2

Apt Packages

Use the apt input to install system packages before the agent runs (Debian/Ubuntu runners only):

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: anthropic/claude-opus-5
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    apt: libwebkit2gtk-4.1-dev libxdo-dev libssl-dev

The downloaded .deb files are cached with actions/cache, keyed on the runner OS, architecture, and the normalized package list. Repeat runs with the same list restore the packages from the cache instead of re-downloading them from the apt mirrors; changing the list produces a new cache key and installs the new set. Caching is best-effort: any cache restore or save failure falls back to a normal uncached install.

Language Toolchains

Use the languages input to install toolchains for your project's languages before the agent runs. The action respects version files when present; otherwise it installs the latest stable version.

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: anthropic/claude-opus-5
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    languages: |
      go
      typescript

Adding Custom Instructions

The action includes comprehensive default instructions for the agent that cover:

  • Creating and posting a plan with todos
  • Real-time progress updates
  • Making file changes
  • Creating branches and commits
  • Opening pull requests
  • Posting results with PR links

You can provide additional project-specific instructions that will be appended to the defaults:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}
    custom-instructions: |
      - Always run tests before committing changes
      - Follow the project's coding style guide in CONTRIBUTING.md
      - Add JSDoc comments for any new functions
      - Update relevant documentation when making changes

The custom instructions enhance the agent's behavior without replacing the core workflow.

Overriding System Prompts

For full control, the system-prompt-* inputs replace the bundled system prompts entirely:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}
    system-prompt-issue: |
      You are an agent for issue #{{issueNumber}}.
      <your full instructions here>

Warning: These inputs replace, not merge with, the bundled defaults. The defaults carry a git-safety block (branch-first, commit-per-todo, push, draft PR, finish checklist) that guards against lost work on the ephemeral runner. An override that omits those instructions silently drops that guard. When enable-git-operations is true the action emits a ::warning:: in the run log listing the missing git-safety markers so the drop is visible. Prefer custom-instructions to layer extras on top of the default unless you need a complete replacement; if you do replace, re-add the branch/commit/push/PR discipline to your text.

Loading Agent Skills

Infer skills are reusable Markdown packages (a folder with a SKILL.md frontmatter file) that the agent loads on startup and invokes by name. The action can install skills before the agent runs:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    skills: |
      maintainer
      # acme/internal-comms
      # https://github.com/anthropics/skills/tree/main/skills/pdf

Each line is passed directly to infer skills install, which accepts three forms:

  • Bare skill name - maintainer resolves to inference-gateway/skills/skills/maintainer/
  • <org>/<skill> pair - acme/internal-comms resolves to acme/skills/skills/internal-comms/
  • Full GitHub tree URL - for any layout, branch, or tag: https://github.com/<owner>/<repo>/tree/<ref>/<path>

Lines beginning with # are treated as comments. Blank lines are ignored.

Notes:

  • Skills are installed to ~/.infer/skills/ on the runner (--user --overwrite). Your working tree is not modified.
  • Providing any skill automatically sets INFER_AGENT_SKILLS_ENABLED=true for the agent run.
  • Skill discovery and the first-party skill catalog live at inference-gateway/skills.
  • Skill installs are authenticated with the github-token you provide (passed to the CLI as GITHUB_TOKEN), so they use the 5,000 requests/hour authenticated limit and can reach private repositories the token can access. Without a token, GitHub's 60 requests/hour-per-IP anonymous limit applies and is easily exhausted on shared CI runners.

Catalog Skill Discovery

By default (skills-discovery: true), the agent sees the full published skill catalog and downloads a skill on demand when a prompt names it. The catalog index is fetched once per process (one cached HTTP GET, 3s timeout, best-effort) and the prompt block listing available skills is hard-capped at 4000 chars, so the cost is bounded regardless of catalog size.

Security caveat for public repositories. In headless CI mode a named skill downloads with no approval gate, since there is nobody to ask. On public repositories the prompt text comes from third-party issue/comment bodies. A comment naming a skill causes the runner to fetch that skill's instructions from the configured skills-repository. The model cannot trigger this on its own; the trigger is always human-authored prompt text. Set skills-discovery: false to disable catalog discovery entirely.

Version pin note. The default version: v0.154.0 includes the bounded prompt block (CLI commit d266380d). If you pin to an earlier release, catalog discovery may regress token cost on large catalogs - upgrade the pin.

Private fork example. Redirect the install shorthand, on-demand downloads, and the catalog index to a private fork in one setting:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    skills-repository: my-org/private-skills

When skills-repository is set, bare skill names like maintainer resolve against my-org/private-skills instead of inference-gateway/skills, and the catalog index is fetched from the same repo. The GitHub token is only ever sent to GitHub hosts.

Loading Infer Plugins

Infer plugins are Claude Code-format packages (an AGENTS.md instructions file plus optional skills) that extend the agent's capabilities. The action can install plugins before the agent runs:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    plugins: |
      # community plugin
      DietrichGebert/ponytail
      # pinned ref
      my-org/my-plugin@v2.1.0
      # full GitHub URL
      https://github.com/my-org/agent-plugins/tree/v1.0

Each line is passed directly to infer plugins install --yes, which accepts three forms:

  • owner/repo - DietrichGebert/ponytail resolves to github.com/DietrichGebert/ponytail
  • owner/repo@ref - pins a specific tag, branch, or commit: my-org/my-plugin@v2.1.0
  • Full GitHub URL - for any layout, branch, or tag: https://github.com/<owner>/<repo>[/tree/<ref>]

Lines beginning with # are treated as comments. Blank lines are ignored.

Security model: Plugins are content-only mapping (skills + instructions). Plugin code (hooks/, commands/, agents/) is detected by the CLI but never executed or installed. This is especially important on unattended CI runners - the action inherits this safety from the CLI.

Notes:

  • Plugins are installed to ~/.infer/plugins/ on the runner with --yes (non-interactive). Your working tree is not modified.
  • A failing install fails the step with a log line naming the entry.
  • After installation, infer plugins list is printed to the job log.
  • Plugin skills surface through normal skills discovery (scope plugin), and each enabled plugin's AGENTS.md is injected into the system prompt as a labeled PLUGIN INSTRUCTIONS (<name>) section.
  • If the pinned CLI version predates the infer plugins subcommand, the install step fails loudly, which is acceptable - upgrade the version pin.
  • No extra wiring is needed beyond the install: plugins are enabled by default in the CLI.

Spinning up A2A Agents

Advanced / experimental. A2A (Agent-to-Agent) lets the main agent delegate sub-tasks to other agents running as local Docker containers on the runner.

The agents input registers one or more A2A agents before the run, starts them as containers, and exposes them to the model via the A2A tools:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    agents: |
      browser-agent
      documentation-agent
      # custom image:
      # my-agent=ghcr.io/my-org/my-agent:latest

Each comma/newline-separated entry is one of:

  • A first-party agent name the CLI knows - browser-agent, mock-agent, google-calendar-agent, documentation-agent, or n8n-agent. The CLI resolves the OCI image and a localhost URL for you, so the bare name is enough.
  • A name=oci-image pair - registers a custom image on an auto-assigned localhost port (e.g. my-agent=ghcr.io/my-org/my-agent:latest).

Lines beginning with # are comments. Blank lines are ignored.

For each entry the action runs infer agents add ... --run --model <model>, enables it, and sets INFER_A2A_ENABLED=true so infer headless starts the containers and can delegate to them. Under the hood this writes the CLI's project-level .infer/agents.yaml, which the action keeps out of the agent's commits (the "Hide Infer workspace from git" step).

Notes:

  • Docker is required on the runner. The default ubuntu-24.04 GitHub-hosted runner has it pre-installed; the agents run as local containers.
  • Registration is best-effort. An unknown name or a failed infer agents add logs a warning and is skipped - it does not fail the run. A missing Docker daemon warns rather than aborting.
  • Agents default to the same model as the main run (the model your workflow already has a provider key for). For per-agent models, configure .infer/agents.yaml directly.
  • Container lifecycle is managed by the CLI; the action also tears down any leftover inference-agent-* containers on cleanup, so they do not leak between runs on self-hosted runners.

Bash Commands (allow-list)

The agent runs bash through the Infer CLI's allow-list. As of CLI v0.121.0 the CLI owns a curated read-only baseline that every run inherits - you no longer configure it here. It covers file reads (ls, find, head, tail, wc, sort, uniq, tree), echo, task, make, read-only git (git status|branch|log|diff|remote|show), and read-only gh - including gh project list|view|item-list|field-list (reading project boards). It contains no writes.

On top of that baseline:

  • When git operations are enabled (the default), the action appends exactly the writes its PR workflow needs: git add/commit/push/checkout/switch/fetch, gh pr create, gh pr ready, and a scoped gh pr edit limited to --title/--body/--body-file. gh pr merge, gh pr close, and gh pr review are deliberately never appended - the agent opens and maintains its own PR but a human reviews and merges.
  • Use bash-allow-append to add your project's tooling. Entries are Go regexes, each anchored to the whole command, comma- or newline-separated:
- uses: inference-gateway/infer-action@v1
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    # Add project tooling on top of the CLI baseline + the action's git-write append:
    bash-allow-append: "npm( .*)?,pnpm( .*)?,node( .*)?,go test( .*)?"

Migrating from older versions (pre-CLI v0.121.0): the bash-whitelist-commands, bash-whitelist-commands-append, bash-whitelist-patterns, and bash-whitelist-patterns-append inputs are removed, along with the action's own default command/pattern lists - the CLI now owns the read-only baseline. Move everything you used to append into the single bash-allow-append input, and rewrite each entry as a regex anchored to the whole command: a bare command name no longer matches its arguments, so npm becomes npm( .*)? and ^npm .* becomes npm( .*)?. To replace the CLI baseline (rather than append to it), configure the CLI's .infer/config.yaml directly.

Security notes:

  • Only allow commands you trust the agent to run.
  • Entries are evaluated as regex (comma- or newline-separated; no commas inside a single pattern).
  • The CLI rejects command chaining, redirects, command substitution, and environment-variable expansion in allowed commands, so an allow entry cannot be widened via ; / && / $() / >.

Example appends:

  • Node.js projects: bash-allow-append: "npm( .*)?,pnpm( .*)?,node( .*)?"
  • Python projects: bash-allow-append: "python3( .*)?,pip( .*)?,pytest( .*)?"
  • Build tools: bash-allow-append: "cargo( .*)?,cmake( .*)?,mvn( .*)?"

Disabling Git Operations (Comment-Only Mode)

By default, the agent can create branches, commits, and pull requests. If you want the agent to only analyze issues and post comments without making code changes, disable git operations:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    enable-git-operations: false

When enable-git-operations: false:

  • The action does not append the git-write commands or gh pr create, so the agent keeps only the CLI's read-only baseline (it can still read with git log / gh ... view, but cannot commit, push, or open a PR)
  • The agent can only analyze code, provide suggestions, and post comments
  • No branches, commits, or pull requests will be created
  • Useful for advisory-only workflows or testing the action safely

Repo Git Hooks

By default the action enables the repository's own git hooks before the agent runs:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
  • On by default (enable-git-hooks: true) - the action runs git config core.hooksPath <hooks-path> on its workspace (default .githooks; override with hooks-path for a non-standard location), so every commit the agent makes triggers the repo-defined pre-commit hook. The repo owns the hook path and contents; the action only turns it on.
  • No hooks, no problem - when the hooks path does not exist (no .githooks directory / no hook), the action skips silently and the run is unaffected.
  • Set enable-git-hooks: false to skip entirely. The salvage step commits with --no-verify and the agent is prompted to fall back to --no-verify over losing unpushed work, so a failing hook never strands the run's output.

Direct Prompt (Manual workflow_dispatch Runs)

By default the action triggers from issues / issue_comment / pull_request_review_comment events and reads the task from the issue or comment body. To run the agent against a free-text task with no issue or comment - for example from a manual workflow_dispatch form - pass the text through direct-prompt:

name: Infer (manual)

on:
  workflow_dispatch:
    inputs:
      prompt:
        description: "Task for the agent to work on"
        required: true
        type: string

permissions:
  contents: write
  pull-requests: write

jobs:
  infer:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v7.0.0

      - uses: inference-gateway/infer-action@main
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          model: deepseek/deepseek-v4-flash
          deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}
          direct-prompt: ${{ inputs.prompt }}

When direct-prompt is non-empty:

  • The agent runs against that text instead of an issue/comment body, so no issues or issue_comment event is required - the action works under workflow_dispatch (or any event).
  • There is no issue/PR thread to reply to, so the agent commits its work to a new branch and opens a pull request, then the run's result and the PR link are written to the workflow job summary (and the PR URL is exposed as the pr-url output).
  • All other inputs (model, skills, max-turns, compact-auto-at, bash-allow-append, provider keys, debug, ...) compose as usual. A /model override embedded in the prompt is honored, just like in event-driven mode.
  • Leave direct-prompt empty (the default) and event-driven behavior is unchanged.

When enable-git-operations: false, direct-prompt runs in advisory mode: the agent only writes its findings to the job summary (no branch or PR).

Persistent Agent Memory

Give the agent a cross-run memory backed by a git repository. The Infer CLI stores durable facts as Markdown files under ~/.infer/memory; with the git backend enabled it pulls that directory from a remote at run start and commits + pushes when a fact changes - so what the agent learns in one issue run is available in the next.

Opt-in and inert by default. When memory-repo is empty no memory env vars are set and nothing changes for existing users. The action only surfaces the inputs, wires up auth for the memory remote, and exports the bot committer identity; the CLI (the source of truth for defaults and sync behavior) does the actual syncing.

Dedicated memory repo over ssh with a deploy key (create the key with write access on the memory repo):

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    memory-repo: git@github.com:my-org/agent-memory.git
    memory-deploy-key: ${{ secrets.MEMORY_DEPLOY_KEY }}

Or over https with a token (a fine-grained PAT or GitHub App installation token with contents: write on the memory repo):

memory-repo: https://github.com/my-org/agent-memory
memory-token: ${{ secrets.MEMORY_TOKEN }}

Lightest setup - a memory branch of the workflow repo itself. When memory-repo is an https URL on the same GitHub instance and no credential input is set, the action falls back to github-token, which can already write to the workflow repo. No extra secret needed - just make sure the job has contents: write:

memory-repo: https://github.com/${{ github.repository }}
memory-branch: agent-memory

Details:

  • Auth scoping. A deploy key is written to ~/.ssh/infer-memory-deploy-key and wired via core.sshCommand; a token is applied as a git insteadOf rewrite scoped to the memory repo URL. Both are removed again in the action's cleanup step, and both credentials are auto-masked in logs and redacted from the cooking comment.
  • Identity. Memory commits are attributed to the same bot identity as the agent's code commits: <github-app-slug>[bot] when set, else github-actions[bot] (exported as GIT_AUTHOR_*/GIT_COMMITTER_*).
  • Defaults live in the CLI. Branch main, pull on start, push on finish, 60s per-git-op timeout, commit message chore(memory): sync. The memory-branch / memory-sync-on-* inputs override them only when set.
  • Concurrent runs pushing the same memory remote are reconciled by the CLI's push -> pull-rebase -> retry loop; no action-side locking is needed.
  • Sync is best-effort - a memory pull/push failure logs a warning and never fails the run. An empty remote is adopted on first push.
  • Independent of enable-git-operations - memory sync happens inside the CLI process, not through the agent's bash allow-list, so it works in comment-only mode too.

Requires Infer CLI >= v0.127.0 (the default version pin already satisfies this).

See examples/with-memory.yml for a complete copy-paste workflow.

OpenTelemetry Observability

The action wires OpenTelemetry across everything that runs in the job: the infer CLI, the in-job gateway, and the A2A agent containers - all correlated into one distributed trace (the CLI propagates traceparent/baggage on every A2A call).

A local collector runs by default. Two modes, combinable:

otel-collectorotel-exporter-otlp-endpointBehavior
true (default)emptyTemporary local collector; traces fan back to the CLI so the result footer shows the full distributed tree
true (default)setLocal collector, forwarding traces/metrics/logs to the remote with otel-exporter-otlp-headers attached
falseemptyNo telemetry beyond the CLI's own local trace files
falsesetCLI, gateway, and agents export directly to the remote endpoint

Local collector (otel-collector: true, the default)

Deploys otel/opentelemetry-collector-contrib as a temporary Docker container (host network) for the duration of the job - this is on by default, set otel-collector: "false" to opt out. The CLI and gateway push to localhost:4318, agent containers to 172.17.0.1:4318, and the collector fans traces back to the CLI's local receiver - so the Traces section in the result footer and step summary shows the full tree, including a2a.request sub-spans inside the agents:

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    agents: mock-agent

Local mode is traces-first: metrics and logs land in the collector's debug exporter (dumped to the job log during cleanup when debug: true). Add otel-exporter-otlp-endpoint to forward all three signals to a real backend. Requires Docker on the runner (Linux; the same requirement as agents). If you want the raw trace files as a build artifact, add a consumer-side step:

- uses: actions/upload-artifact@v7.0.1
  if: always()
  with:
    name: infer-traces
    path: ~/.infer/telemetry/*.jsonl

Remote endpoint (otel-exporter-otlp-endpoint)

- uses: inference-gateway/infer-action@main
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    otel-exporter-otlp-endpoint: http://my-collector:4318
    otel-exporter-otlp-headers: "Authorization=Bearer my-otel-token"

Note: in remote-only mode the headers are attached to the CLI's exports but not to the gateway's or agents' - set otel-collector: "true" alongside the endpoint if your backend requires authentication on every producer (the collector then attaches the headers on the forwarding leg).

Resource attributes

The CLI adds GitHub Actions context (actor, repo, run id, workflow url) to OTEL_RESOURCE_ATTRIBUTES automatically. Additional attributes can be passed via the otel-resource-attributes input.

Best-effort & safe

  • Telemetry never blocks or fails the run; a missing Docker or a failed collector start logs a warning and continues without it.
  • The otel-exporter-otlp-headers input is secret and auto-masked.
  • Honors dry-run: the OTel setup step is skipped entirely.
  • The collector container is removed in the cleanup step. A hard cancel before cleanup can leave it running - harmless on ephemeral GitHub-hosted runners, worth knowing on self-hosted ones (ports 4318/4319 must also be free there, and 172.17.0.1 assumes the default Docker bridge).
name: Infer (manual)

on:
  workflow_dispatch:
    inputs:
      prompt:
        description: "Task for the agent to work on"
        required: true
        type: string

permissions:
  contents: write
  pull-requests: write

jobs:
  infer:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v7.0.0

      - uses: inference-gateway/infer-action@main
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          model: deepseek/deepseek-v4-flash
          deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}
          direct-prompt: ${{ inputs.prompt }}

When direct-prompt is non-empty:

  • The agent runs against that text instead of an issue/comment body, so no issues or issue_comment event is required - the action works under workflow_dispatch (or any event).
  • There is no issue/PR thread to reply to, so the agent commits its work to a new branch and opens a pull request, then the run's result and the PR link are written to the workflow job summary (and the PR URL is exposed as the pr-url output).
  • All other inputs (model, skills, max-turns, compact-auto-at, bash-allow-append, provider keys, debug, ...) compose as usual. A /model override embedded in the prompt is honored, just like in event-driven mode.
  • Leave direct-prompt empty (the default) and event-driven behavior is unchanged.

When enable-git-operations: false, direct-prompt runs in advisory mode: the agent only writes its findings to the job summary (no branch or PR).

Working with images

Two independent image capabilities, both powered by the Infer CLI:

Reading images (screenshots in issues/PRs)

Opt in by setting vision-model to any vision-capable model your gateway serves (requires CLI >= v0.159.0):

- uses: inference-gateway/infer-action@main
  with:
    model: deepseek/deepseek-chat # the session model does NOT need vision
    vision-model: anthropic/claude-haiku-4-5-20251001
    anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    deepseek-api-key: ${{ secrets.DEEPSEEK_API_KEY }}

This enables the CLI's ImageDecode tool (a side-call to vision-model through the gateway) and adds guidance to the system prompt: when the issue or PR text embeds an image (![...](url)), the agent downloads it with WebFetch and reads it with ImageDecode before starting work. The default web-fetch-domains already include the githubusercontent.com hosts that github.com/user-attachments links redirect to.

Limitation: attachments on private repositories redirect to signed URLs that WebFetch requests unauthenticated, so private-repo screenshots may fail to download; the agent is instructed to say so and continue with the text. Public repositories work.

Generating images

The CLI's ImageGeneration/ImageEdit/ImageVariation tools are on by default and call the gateway's /v1/images/* endpoints with openai/gpt-image-2 - so image generation already works if you pass openai-api-key. Set image-model to use a different image model. Generated images land in .infer/artifacts and surface through Run artifacts: uploaded with the run artifact and embedded inline in the result comment.

Run artifacts

Files the agent downloads or produces during a run land in the Infer CLI's artifact directories (.infer/artifacts in the workspace and ~/.infer/artifacts) - for example a screenshot an A2A browser agent produced that the agent WebFetched, or any binary content the CLI auto-saved. With upload-artifacts: true (the default), the action harvests those directories after the run:

  • Files matching artifact-extensions (case-insensitive; default covers common image, text, and document types) are uploaded as a single GitHub Actions run artifact named infer-artifacts-<run_id>, visible on the run's summary page.
  • The result comment gets an Artifacts section: images are embedded inline, other files are listed with sizes next to a download link for the run artifact.
  • To make images render in the comment, the action pushes them to an infer-artifacts branch in the repository (under <run_id>/) and embeds their raw URLs - run artifacts themselves are auth-gated zips and cannot serve inline images. If that push fails (fork PRs, read-only tokens) or the repository is private (raw URLs don't render there), images gracefully fall back to the plain file list. Up to 10 images of at most 25 MB each are embedded per run.
  • When the run produces nothing, there is no upload and no Artifacts section.

Set upload-artifacts: "false" to turn the whole feature off.

Dry-run / Local Testing

Set dry-run: true to run the whole action in a plan-only mode - ideal for trying a workflow locally with act before it ever runs for real:

- uses: ./ # or inference-gateway/infer-action@<tag>
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    model: deepseek/deepseek-v4-flash
    dry-run: true

In dry-run the action:

  • Forces the bundled mock agent - no real CLI install and no provider token, so it composes with any model without spending anything.
  • Simulates every GitHub mutation. Instead of creating/updating a comment, the eyes reaction, the "I'm cooking..." comment, comment zones, or the spinner, it prints a [dry-run] would ... line (e.g. [dry-run] would create a github issue comment on issue #1 (https://github.com/owner/repo/issues/1)). Secret values are still redacted in the printed bodies.
  • Prints a DRY RUN banner with the exact SYSTEM / TASK / REMINDER prompts and the resolved bash allow-list append (the entries the action adds on top of the CLI's read-only baseline) the agent would receive.
  • Keeps GitHub reads real, so you see the actual target issue/PR/comment thread. Reads degrade gracefully when no token is available (a public-repo read still works unauthenticated; otherwise it warns and continues with env-derived data).

Ready-to-run example workflows and the task test:issue | test:comment | test:direct helpers live in examples/local/ - see examples/README.md.

Complete Workflow Example

name: Infer Action

on:
  issues:
    types:
      - opened
      - edited
  issue_comment:
    types:
      - created
  pull_request_review_comment:
    types:
      - created
permissions:
  issues: write
  contents: write
  pull-requests: write

jobs:
  infer:
    runs-on: ubuntu-24.04

    steps:
      - name: Checkout repository
        uses: actions/checkout@v7.0.0

      - name: Run Infer Agent
        uses: inference-gateway/infer-action@main
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          trigger-phrase: "@infer"
          model: deepseek/deepseek-v4-flash
          anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          max-turns: 50

How It Works

  1. Trigger Detection: The action monitors issues, comments, and inline PR review comments for your configured trigger phrase (default: @infer). Optionally, you can specify a model override using /model provider/model-name in the trigger message. Context stays lean: conversation triggers include the opening body plus the last 3 human comments (older ones are summarised as an "N earlier comments omitted" note), and a ping on an inline review comment gives the agent only that code section (file, line, diff hunk) - when the ping is a reply, the whole review thread is included so earlier suggestions stay visible
  2. Skill Loading (optional): If the skills input lists any skills, they are installed into the runner's user-global skill directory (~/.infer/skills/) and enabled for the agent
  3. A2A Agent Setup (optional): If the agents input lists any A2A agents, they are registered and enabled, A2A is turned on, and infer headless starts each one as a local Docker container the model can delegate to (see Spinning up A2A Agents)
  4. Plan Creation: The agent uses TodoWrite to track its plan; the action's runner mirrors the todos to the issue comment in real time as the agent makes progress
  5. Agent Execution: The agent runs with your specified model (or the override model if provided). For code-change requests, the agent creates the fix/issue-{number} working branch and pushes it before any file edits, then commits and pushes after each completed todo so partial work survives even if the run is cut short. The runner configures the CLI's native reminders (passed via INFER_REMINDERS_CONFIG with merge: true, CLI >= v0.130.0): a periodic nudge to keep the branch, draft PR, and pushes current, a wrap-up reminder near the turn limit telling the agent to commit and push everything now, and a post_tool on_failure nudge that re-states the "a failed call means the change did not happen" rule after each failed tool call on writable runs. These merge onto the CLI's built-in defaults, so the built-in todo-hygiene and memory reminders stay intact. Power users can pass a full reminders-config YAML to take over entirely
  6. Pull Request Creation: The agent opens its own pull request with gh pr create --body-file (writing the description to a file first to avoid shell-quoting problems) once its work is committed and pushed. After the agent exits, the recover step looks up the open PR for the branch and adds its URL to the issue comment; if the agent left a thin body (e.g. a bare Fixes #{number}), it backfills a real summary from the commit log. The agent is blocked from merging, closing, editing, or reviewing PRs
  7. Result Posting: The action posts a final summary to the same issue comment with:
    • Status icon (success / failure) and exit code
    • The agent's final response, shown as a visible (non-collapsed) section directly under the status header
    • The model that was used
    • Token usage for the run (prompt / completion / total, plus request count)
    • Per-session cost for the run (input / output / total), when the CLI reports pricing
    • Total tool calls made, with the run's success rate
    • Any failed tool calls (collapsed)

Pull Request Workflow

When the agent needs to make code changes to resolve an issue, the agent owns the git/PR flow on the happy path - with an always() recover step as a model-independent safety net that recovers the work if the agent skips it (or if the job times out before it finishes):

  1. Agent creates the working branch fix/issue-{number} and pushes it before any file edits - this is the first thing the agent does for any code-change request, so partial progress is recoverable if the run ends early
  2. Agent commits and pushes after each completed todo - using Conventional Commits - rather than batching everything to the end, after running the repo's own checks (lint / format / tests) and fixing failures
  3. Agent opens the pull request with gh pr create --body-file once its work is pushed, writing the description to a file first (to avoid shell-quoting problems) - the title plus a real body (Resolves #{number}, a ## Summary, and a ## Changes list)
  4. The recover step links the PR in the issue comment by looking up the open PR for the branch after the agent exits. As a safety net it backfills the PR body from the commit log when the agent left it thin (empty or a bare Fixes #{number})
  5. The salvage step rescues unpushed work on every outcome - when a weak model skips the flow above, when the job times out mid-run, and when the agent exits "successfully" without ever pushing. If the agent edited files but never branched/committed/pushed/opened a PR (or left commits unpushed), it commits the work onto a fix/issue-{number} branch (never main/master), pushes it, and opens a draft PR titled โ€ฆ (salvaged) - so your work is never lost just because the model ignored its instructions or the run was cut short. On a pull-request run it pushes the leftover work to the existing PR branch instead. It never opens a duplicate (an existing open/merged/closed PR for the branch blocks creation) and never merges. Because it runs as an always() step it survives a job timeout-minutes cancellation that kills the agent mid-run. A salvaged run is reported as a โš ๏ธ stopped-early status, never โœ… (see the stopped-early / timed-out outputs)

The runner is ephemeral: the branch-first / commit-per-todo discipline plus the always() recover step are what make the workflow resilient to mid-run termination, job timeout-minutes cancellations, max-turns limits, and provider errors. Because CI runs only after the job ends, the agent runs the repo's checks locally before committing rather than relying on CI feedback it can't see.

Required Permissions

For the pull request workflow to work, your workflow must have these permissions:

permissions:
  issues: write # Post comments and read issue details
  contents: write # Create branches and commit changes
  pull-requests: write # Create pull requests

Inputs

InputDescriptionRequiredDefault
github-tokenGitHub token for API accessYes-
github-app-slugSlug of the GitHub App whose bot identity authors the agent's commits (e.g. infer-bot); resolved via GET /users/{slug}[bot]. Falls back to github-actions[bot] when empty or on failureNo''
trigger-phrasePhrase to trigger the agentNo@infer
direct-promptFree-text task to run directly (bypasses issue/comment triggers; enables workflow_dispatch runs)No''
modelAI model to useYes-
versionInfer CLI version to installNov0.188.0
ollama-cloud-api-keyOllama Cloud API keyNo*-
groq-api-keyGroq API keyNo*-
llamacpp-api-keyllama.cpp API keyNo*-
openai-api-keyOpenAI API keyNo*-
cloudflare-api-keyCloudflare API keyNo*-
cohere-api-keyCohere API keyNo*-
anthropic-api-keyAnthropic API keyNo*-
deepseek-api-keyDeepSeek API keyNo*-
google-api-keyGoogle API keyNo*-
mistral-api-keyMistral API keyNo*-
minimax-api-keyMiniMax API keyNo*-
moonshot-api-keyMoonshot API keyNo*-
nvidia-api-keyNvidia API keyNo*-
zai-api-keyZai API keyNo*-
llamacpp-api-urlllama.cpp API URLNo*-
ollama-api-urlOllama API URLNo*-
max-turnsMaximum agent iterationsNo150
custom-instructionsAdditional instructions appended to default behaviorNo''
reminders-configVerbatim reminders YAML passed to the CLI via INFER_REMINDERS_CONFIG, REPLACING the composed default. Use to take full control of the CLI's native reminders (hooks, triggers, cadences). Add merge: true to layer onto the CLI's built-in defaults instead of replacing them. Needs CLI >= v0.130.0; see the CLI config/reminders.go for the schemaNo''
skillsNewline-separated list of skills installed via infer skills install. Auto-enables skills.No''
skills-discoveryEnable catalog skill discovery (default: true). When true, the agent sees the full published skill catalog and downloads a skill on demand when a prompt names it. Sets INFER_AGENT_SKILLS_ENABLED=true. Security: In headless CI a named skill downloads with no approval gate. On public repos the prompt text comes from third parties. Set to false to disable. Requires CLI >= v0.154.0.Notrue
skills-repositoryGitHub repository hosting the skill catalog (default: inference-gateway/skills). Redirects install shorthand, on-demand downloads, and the catalog index in one setting. Maps to INFER_AGENT_SKILLS_REPOSITORY.Noinference-gateway/skills
pluginsNewline-separated list of plugins installed via infer plugins install --yes. Content-only mapping (skills + instructions); plugin code is never executed. See Loading Infer PluginsNo''
agentsComma/newline-separated list of A2A agents to run as local Docker containers (first-party names like browser-agent, or name=oci-image pairs). Registers + enables each, turns on A2A, and defaults them to the main model. Requires Docker. See Spinning up A2A AgentsNo''
bash-allow-appendGo regex entries appended to the CLI's read-only bash allow-list (e.g., npm( .*)?,pnpm( .*)?); each is anchored to the whole command. See Bash CommandsNo''
web-fetch-domainsDomains the WebFetch tool may use; passed as the INFER_TOOLS_WEB_FETCH_ALLOWED_DOMAINS env var (maps to tools.web_fetch.allowed_domains, replaces the CLI default). Empty = github.com,raw.githubusercontent.com,api.github.com plus the githubusercontent.com hosts issue/PR image attachments redirect toNo''
vision-modelprovider/model of a vision-capable model for the CLI's image annotator (INFER_VISION_ANNOTATOR_MODEL + INFER_VISION_ANNOTATOR_ENABLED=true). Enables the ImageDecode tool so the agent can read screenshots/diagrams embedded in issues and PRs. Needs that provider's API key and CLI >= v0.159.0. Empty = image understanding off. See Working with imagesNo''
image-modelprovider/model for the ImageGeneration/ImageEdit/ImageVariation tools' one-off /v1/images/* requests (INFER_TOOLS_IMAGE_GENERATION_MODEL etc.). Empty = CLI default (openai/gpt-image-2, needs OPENAI_API_KEY). See Working with imagesNo''
aptNewline- or space-separated list of apt packages to install before the agent runs (Debian/Ubuntu runners only). Downloaded .deb files are cached with actions/cache, keyed on the runner OS, architecture, and the normalized package list; repeat runs with the same list skip the re-download, and cache failures fall back to a normal uncached install. See Apt PackagesNo''
languagesNewline- or space-separated list of programming languages whose toolchains to install before the agent runs. Supported: go, rust, node (alias: typescript), python. When the project has a version file (go.mod, .nvmrc, .python-version) it is respected; otherwise latest stable. See Language ToolchainsNo''
memory-repoGit remote URL backing the agent's persistent cross-run memory (ssh or https). Enables the CLI's memory git backend: pull on run start, commit + push when a fact changes. Empty = feature off. See Persistent Agent MemoryNo''
memory-branchBranch of memory-repo to sync (INFER_MEMORY_BACKEND_GIT_BRANCH). Empty = CLI default (main)No''
memory-sync-on-startPull memory at run start: pull or off (INFER_MEMORY_BACKEND_GIT_SYNC_ON_START). Empty = CLI default (pull)No''
memory-sync-on-finishPush memory changes at run finish: push or off (INFER_MEMORY_BACKEND_GIT_SYNC_ON_FINISH). Empty = CLI default (push)No''
memory-deploy-keySSH private key (e.g. a deploy key with write access) authenticating an ssh memory-repo. Secret, auto-masked. See Persistent Agent MemoryNo''
memory-tokenToken authenticating an https memory-repo (scoped git insteadOf rewrite). Secret, auto-masked. Empty on a same-instance https URL = falls back to github-tokenNo''
enable-git-operationsEnable git operations and PR creation. Set to false for comment-only modeNotrue
enable-git-hooksEnable the repo's own git hooks before the agent runs (git config core.hooksPath <hooks-path>) so the agent's commits trigger its pre-commit; skipped silently when the hooks path does not exist. See Repo Git HooksNotrue
hooks-pathHooks directory wired to git config core.hooksPath while enable-git-hooks is true (workspace-local only)No.githooks
review-inlineWhen true and the run is in review mode, post findings as a real GitHub PR review with inline, line-anchored comments (including suggestion blocks) instead of a single conversation comment. See Review ModeNofalse
debugEnable debug logs, stdout stream events (reminder injection, compaction triggers), and stdout transcript mirroring (unless mirror-agent-logs: "false")Nofalse
compact-auto-atAuto-compaction threshold as % of model context window. Valid range 20-100No50
mirror-agent-logsMirror the agent's verbose stdout transcript to the workflow log. Empty (the default) follows debug; set "false" to stay muted even in debug, "true" to mirror regardless. stderr (crashes, stack-traces) is always mirrored regardless. The /tmp/agent-output.txt file that post-results reads for the comment footer is always written. A minimal heartbeat still prints.No"" (follows debug)
show-footerShow the result footer in the cooking comment. When false, the entire result section (status header, agent response, metadata, token usage, cost, tool-call stats, traces, logs, and the attribution line) is omitted from the comment. The step summary and action outputs are still written regardlessNotrue
upload-artifactsCollect files the agent left in the Infer artifact directories (.infer/artifacts and ~/.infer/artifacts) after the run, upload them as a run artifact (infer-artifacts-<run_id>), and render an Artifacts section in the result comment - images embedded inline (via the repo's infer-artifacts branch), other files listed with the download link. See Run artifactsNotrue
artifact-extensionsComma-separated, case-insensitive list of file extensions eligible for collection when upload-artifacts is enabledNopng,jpg,... (see action)
dry-runPlan-only local-testing mode: forces the bundled mock agent, simulates every GitHub mutation ([dry-run] would ...), prints the SYSTEM/TASK/REMINDER prompts and bash allow-list; reads runNofalse
mock-agent-scenarioMock scenario the bundled mock agent runs when dry-run: true - happy, failures, no-todos, or emptyNohappy
otel-exporter-otlp-endpointOpenTelemetry OTLP HTTP endpoint (e.g. http://localhost:4318). Empty = disabled (default). Passed through to the infer CLI subprocess. Maps to OTEL_EXPORTER_OTLP_ENDPOINT.No''
otel-exporter-otlp-headersComma-separated key=value headers for OTLP HTTP requests (e.g. Authorization=Bearer my-token). Secret, auto-masked. Passed through to the infer CLI subprocess. Maps to OTEL_EXPORTER_OTLP_HEADERS.No''
otel-service-nameValue for the service.name resource attribute. Defaults to infer-action. Passed through to the infer CLI subprocess. Maps to OTEL_SERVICE_NAME.Noinfer-action
otel-resource-attributesExtra resource attributes in key=val,key2=val2 format. Passed through to the infer CLI subprocess. Maps to OTEL_RESOURCE_ATTRIBUTES. The CLI also adds GitHub Actions context automatically.No''
otel-collectorDeploy a temporary OTel collector (Docker, host network) for the job. CLI, in-job gateway, and A2A agents push traces/metrics/logs to it; traces fan back to the CLI so the result footer shows the full distributed tree. Combines with otel-exporter-otlp-endpoint to forward all signals to a remote with headers attached. On by default; set to false to opt out. Best-effort, never fails.Notrue

* Required if using the corresponding provider

Outputs

OutputDescription
resultHuman-readable result message
exit-codeExit code from the agent command (normalised to 0 on a job-timeout stop, where the work is recovered)
pr-urlURL of the pull request the agent opened, or the draft PR the recover step opened for left-behind work (empty if none)
run-duration-msWall-clock duration of the agent run in milliseconds (0 if unavailable)
stopped-earlytrue if the agent stopped before finishing (unfinished todos, uncommitted or unpushed work, a job-timeout stop, or work the salvage step had to rescue)
timed-outtrue if the job hit its timeout-minutes before the agent finished - work is recovered into a draft PR and reported as โš ๏ธ stopped early, not a failure
failed-tool-calls-countNumber of failed tool calls detected in agent output
total-tool-calls-countTotal number of tool calls made by the agent

Supported Models

  • Anthropic: deepseek/deepseek-v4-flash, anthropic/claude-opus-4-8, etc.
  • OpenAI: openai/gpt-5, openai/gpt-5-mini
  • Google: google/gemini-3-pro, google/gemini-3-flash
  • Moonshot: moonshot/kimi-k2, moonshot/kimi-k2-thinking, moonshot/moonshot-v1-128k

Security Best Practices

  1. Never commit API keys - Always use GitHub Secrets
  2. Use minimal permissions - Only grant necessary workflow permissions
  3. Review agent outputs - Monitor what the agent posts to your issues
  4. Set appropriate max-turns - Prevent runaway execution
  5. Carefully extend the bash allow-list - Only add commands you trust the AI to execute via bash-allow-append. The CLI's read-only baseline is safe by default

Setting Up Secrets

  1. Go to your repository settings
  2. Navigate to "Secrets and variables" โ†’ "Actions"
  3. Click "New repository secret"
  4. Add your API key(s):
    • ANTHROPIC_API_KEY
    • OPENAI_API_KEY
    • GOOGLE_API_KEY

Troubleshooting

Action doesn't trigger

  • Ensure your trigger phrase matches exactly (case-sensitive)
  • Check that the workflow has proper event triggers (issues, issue_comment, pull_request_review_comment)
  • Verify workflow permissions include issues: write

Agent fails to run

  • Check that the correct API key is provided for your chosen model
  • Verify the model name is correct
  • Review the action logs for specific error messages

Comments not posting

  • Ensure github-token has write permissions
  • Check that the workflow has issues: write permission

Pull requests not being created

  • Verify the workflow has contents: write permission for branch creation
  • Ensure the workflow has pull-requests: write permission
  • Check that the repository allows pull requests from workflows
  • Review the action logs for git/gh CLI errors

License

Apache-2.0

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support