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).
- 🤖 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
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 }}- uses: inference-gateway/infer-action@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
model: anthropic/claude-opus-5
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}- uses: inference-gateway/infer-action@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
model: openai/gpt-5
openai-api-key: ${{ secrets.OPENAI_API_KEY }}- uses: inference-gateway/infer-action@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
model: google/gemini-3-pro
google-api-key: ${{ secrets.GOOGLE_API_KEY }}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"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-flashopenai/gpt-5google/gemini-3-prodeepseek/deepseek-v4-flashollama_cloud/qwen3-coder:480bmoonshot/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.
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.
- 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- 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.2The 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 changesThe custom instructions enhance the agent's behavior without replacing the core workflow.
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-operationsis true the action emits a::warning::in the run log listing the missing git-safety markers so the drop is visible. Prefercustom-instructionsto 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.
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/pdfEach line is passed directly to infer skills install, which accepts three forms:
- Bare skill name -
maintainerresolves toinference-gateway/skills/skills/maintainer/ <org>/<skill>pair -acme/internal-commsresolves toacme/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=truefor the agent run. - Skill discovery and the first-party skill catalog live at inference-gateway/skills.
- Skill installs are authenticated with the
github-tokenyou provide (passed to the CLI asGITHUB_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.
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-skillsWhen 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.
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.0Each line is passed directly to infer plugins install --yes, which accepts three forms:
owner/repo-DietrichGebert/ponytailresolves togithub.com/DietrichGebert/ponytailowner/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 listis printed to the job log. - Plugin skills surface through normal skills discovery (scope
plugin), and each enabled plugin'sAGENTS.mdis injected into the system prompt as a labeledPLUGIN INSTRUCTIONS (<name>)section. - If the pinned CLI version predates the
infer pluginssubcommand, the install step fails loudly, which is acceptable - upgrade theversionpin. - No extra wiring is needed beyond the install: plugins are enabled by default in the CLI.
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:latestEach comma/newline-separated entry is one of:
- A first-party agent name the CLI knows -
browser-agent,mock-agent,google-calendar-agent,documentation-agent, orn8n-agent. The CLI resolves the OCI image and a localhost URL for you, so the bare name is enough. - A
name=oci-imagepair - 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 agent 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.04GitHub-hosted runner has it pre-installed; the agents run as local containers. - Registration is best-effort. An unknown name or a failed
infer agents addlogs a warning and is skipped - it does not fail the run. A missing Docker daemon warns rather than aborting. - Agents default to the same
modelas the main run (the model your workflow already has a provider key for). For per-agent models, configure.infer/agents.yamldirectly. - 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.
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 scopedgh pr editlimited to--title/--body/--body-file.gh pr merge,gh pr close, andgh pr revieware deliberately never appended - the agent opens and maintains its own PR but a human reviews and merges. - Use
bash-allow-appendto 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, andbash-whitelist-patterns-appendinputs 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 singlebash-allow-appendinput, and rewrite each entry as a regex anchored to the whole command: a bare command name no longer matches its arguments, sonpmbecomesnpm( .*)?and^npm .*becomesnpm( .*)?. To replace the CLI baseline (rather than append to it), configure the CLI's.infer/config.yamldirectly.
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( .*)?"
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: falseWhen 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 withgit 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
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
issuesorissue_commentevent is required - the action works underworkflow_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-urloutput). - All other inputs (
model,skills,max-turns,compact-auto-at,bash-allow-append, provider keys,debug, ...) compose as usual. A/modeloverride embedded in the prompt is honored, just like in event-driven mode. - Leave
direct-promptempty (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).
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-memoryDetails:
- Auth scoping. A deploy key is written to
~/.ssh/infer-memory-deploy-keyand wired viacore.sshCommand; a token is applied as a gitinsteadOfrewrite 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, elsegithub-actions[bot](exported asGIT_AUTHOR_*/GIT_COMMITTER_*). - Defaults live in the CLI. Branch
main, pull on start, push on finish, 60s per-git-op timeout, commit messagechore(memory): sync. Thememory-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
versionpin already satisfies this).
See examples/with-memory.yml for a complete copy-paste
workflow.
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-collector |
otel-exporter-otlp-endpoint |
Behavior |
|---|---|---|
true (default) |
empty | Temporary local collector; traces fan back to the CLI so the result footer shows the full distributed tree |
true (default) |
set | Local collector, forwarding traces/metrics/logs to the remote with otel-exporter-otlp-headers attached |
false |
empty | No telemetry beyond the CLI's own local trace files |
false |
set | CLI, gateway, and agents export directly to the remote endpoint |
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-agentLocal 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- 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).
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.
- 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-headersinput 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.1assumes 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
issuesorissue_commentevent is required - the action works underworkflow_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-urloutput). - All other inputs (
model,skills,max-turns,compact-auto-at,bash-allow-append, provider keys,debug, ...) compose as usual. A/modeloverride embedded in the prompt is honored, just like in event-driven mode. - Leave
direct-promptempty (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).
Files the agent downloads or produces during a run land in the Infer CLI's tmp
directories (.infer/tmp in the workspace and ~/.infer/tmp) - 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 namedinfer-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-artifactsbranch 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.
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: trueIn dry-run the action:
- Forces the bundled mock agent - no real CLI install and no provider token,
so it composes with any
modelwithout 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/REMINDERprompts 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.
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- 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-namein 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 - Skill Loading (optional): If the
skillsinput lists any skills, they are installed into the runner's user-global skill directory (~/.infer/skills/) and enabled for the agent - A2A Agent Setup (optional): If the
agentsinput lists any A2A agents, they are registered and enabled, A2A is turned on, andinfer agentstarts each one as a local Docker container the model can delegate to (see Spinning up A2A Agents) - 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
- 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 viaINFER_REMINDERS_CONFIGwithmerge: 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 apost_toolon_failurenudge 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 fullreminders-configYAML to take over entirely - 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, therecoverstep 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 bareFixes #{number}), it backfills a real summary from the commit log. The agent is blocked from merging, closing, editing, or reviewing PRs - 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)
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):
- 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 - 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
- Agent opens the pull request with
gh pr create --body-fileonce 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## Changeslist) - 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}) - 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 (nevermain/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 analways()step it survives a jobtimeout-minutescancellation that kills the agent mid-run. A salvaged run is reported as a⚠️ stopped-early status, never ✅ (see thestopped-early/timed-outoutputs)
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.
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| Input | Description | Required | Default |
|---|---|---|---|
github-token |
GitHub token for API access | Yes | - |
github-app-slug |
Slug 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 failure |
No | '' |
trigger-phrase |
Phrase to trigger the agent | No | @infer |
direct-prompt |
Free-text task to run directly (bypasses issue/comment triggers; enables workflow_dispatch runs) |
No | '' |
model |
AI model to use | Yes | - |
version |
Infer CLI version to install | No | v0.158.0 |
ollama-cloud-api-key |
Ollama Cloud API key | No* | - |
groq-api-key |
Groq API key | No* | - |
llamacpp-api-key |
llama.cpp API key | No* | - |
openai-api-key |
OpenAI API key | No* | - |
cloudflare-api-key |
Cloudflare API key | No* | - |
cohere-api-key |
Cohere API key | No* | - |
anthropic-api-key |
Anthropic API key | No* | - |
deepseek-api-key |
DeepSeek API key | No* | - |
google-api-key |
Google API key | No* | - |
mistral-api-key |
Mistral API key | No* | - |
minimax-api-key |
MiniMax API key | No* | - |
moonshot-api-key |
Moonshot API key | No* | - |
nvidia-api-key |
Nvidia API key | No* | - |
zai-api-key |
Zai API key | No* | - |
llamacpp-api-url |
llama.cpp API URL | No* | - |
ollama-api-url |
Ollama API URL | No* | - |
max-turns |
Maximum agent iterations | No | 150 |
custom-instructions |
Additional instructions appended to default behavior | No | '' |
reminders-config |
Verbatim 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 schema |
No | '' |
skills |
Newline-separated list of skills installed via infer skills install. Auto-enables skills. |
No | '' |
skills-discovery |
Enable 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. |
No | true |
skills-repository |
GitHub 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. |
No | inference-gateway/skills |
plugins |
Newline-separated list of plugins installed via infer plugins install --yes. Content-only mapping (skills + instructions); plugin code is never executed. See Loading Infer Plugins |
No | '' |
agents |
Comma/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 Agents |
No | '' |
bash-allow-append |
Go 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 Commands |
No | '' |
web-fetch-domains |
Domains 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 |
No | '' |
memory-repo |
Git 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 Memory | No | '' |
memory-branch |
Branch of memory-repo to sync (INFER_MEMORY_BACKEND_GIT_BRANCH). Empty = CLI default (main) |
No | '' |
memory-sync-on-start |
Pull memory at run start: pull or off (INFER_MEMORY_BACKEND_GIT_SYNC_ON_START). Empty = CLI default (pull) |
No | '' |
memory-sync-on-finish |
Push memory changes at run finish: push or off (INFER_MEMORY_BACKEND_GIT_SYNC_ON_FINISH). Empty = CLI default (push) |
No | '' |
memory-deploy-key |
SSH private key (e.g. a deploy key with write access) authenticating an ssh memory-repo. Secret, auto-masked. See Persistent Agent Memory |
No | '' |
memory-token |
Token authenticating an https memory-repo (scoped git insteadOf rewrite). Secret, auto-masked. Empty on a same-instance https URL = falls back to github-token |
No | '' |
enable-git-operations |
Enable git operations and PR creation. Set to false for comment-only mode |
No | true |
debug |
Enable debug logs and stdout stream events (reminder injection, compaction triggers) | No | false |
compact-auto-at |
Auto-compaction threshold as % of model context window. Valid range 20-100 | No | 50 |
mirror-agent-logs |
Mirror the agent's verbose stdout transcript to the workflow log; defaults to false (suppressed). Set true to mirror it. 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 | false |
show-footer |
Show 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 regardless |
No | true |
upload-artifacts |
Collect files the agent left in the Infer tmp directories (.infer/tmp and ~/.infer/tmp) 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 artifacts |
No | true |
artifact-extensions |
Comma-separated, case-insensitive list of file extensions eligible for collection when upload-artifacts is enabled |
No | png,jpg,... (see action) |
dry-run |
Plan-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 run |
No | false |
mock-agent-scenario |
Mock scenario the bundled mock agent runs when dry-run: true - happy, failures, no-todos, or empty |
No | happy |
otel-exporter-otlp-endpoint |
OpenTelemetry 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-headers |
Comma-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-name |
Value for the service.name resource attribute. Defaults to infer-action. Passed through to the infer CLI subprocess. Maps to OTEL_SERVICE_NAME. |
No | infer-action |
otel-resource-attributes |
Extra 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-collector |
Deploy 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. |
No | true |
* Required if using the corresponding provider
| Output | Description |
|---|---|
result |
Human-readable result message |
exit-code |
Exit code from the agent command (normalised to 0 on a job-timeout stop, where the work is recovered) |
pr-url |
URL of the pull request the agent opened, or the draft PR the recover step opened for left-behind work (empty if none) |
run-duration-ms |
Wall-clock duration of the agent run in milliseconds (0 if unavailable) |
stopped-early |
true 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-out |
true if the job hit its timeout-minutes before the agent finished - work is recovered into a draft PR and reported as |
failed-tool-calls-count |
Number of failed tool calls detected in agent output |
total-tool-calls-count |
Total number of tool calls made by the agent |
- 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
- Never commit API keys - Always use GitHub Secrets
- Use minimal permissions - Only grant necessary workflow permissions
- Review agent outputs - Monitor what the agent posts to your issues
- Set appropriate max-turns - Prevent runaway execution
- 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
- Go to your repository settings
- Navigate to "Secrets and variables" → "Actions"
- Click "New repository secret"
- Add your API key(s):
ANTHROPIC_API_KEYOPENAI_API_KEYGOOGLE_API_KEY
- 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
- 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
- Ensure
github-tokenhas write permissions - Check that the workflow has
issues: writepermission
- Verify the workflow has
contents: writepermission for branch creation - Ensure the workflow has
pull-requests: writepermission - Check that the repository allows pull requests from workflows
- Review the action logs for git/gh CLI errors
Apache-2.0
Contributions are welcome! Please feel free to submit a Pull Request.