10 KiB
@elizaos/plugin-github
GitHub integration for Eliza agents: pull request listing and review, issue lifecycle management, and notification triage via the GitHub REST API.
Purpose / role
Adds GitHub capabilities to any Eliza agent. The plugin is opt-in — add "@elizaos/plugin-github" to the agent's plugin list. It registers a GitHubService (Octokit REST client pool), three exposed action handlers promoted under one umbrella GITHUB action, five API routes for credential management (PAT paste + OAuth device sign-in — the guided setup step behind the Settings → Coding Agents GitHub card, #15796), and a search category for PR lookup.
Plugin surface
Actions (registered via promoteSubactionsToActions(githubAction))
The umbrella action GITHUB dispatches to three sub-actions based on the action parameter:
| Action name | Constant | Sub-actions / ops | Default identity | Confirmation |
|---|---|---|---|---|
GITHUB |
umbrella | routes to all ops below | per op | per op |
GITHUB_ISSUE |
GitHubActions.GITHUB_ISSUE_OP |
create, assign, close, reopen, comment, label |
agent |
required (requireConfirmation) |
GITHUB_PR |
GitHubActions.GITHUB_PR_OP |
list, review |
agent (list) / user (review) |
required for review |
GITHUB_NOTIFICATION_TRIAGE |
GitHubActions.GITHUB_NOTIFICATION_TRIAGE |
reads + scores unread notifications | user |
none (read-only) |
All actions gate on contextGate: { anyOf: ["code", "tasks", "connectors", "automation"] } and roleGate: { minRole: "USER" }.
Services
| Service class | serviceType |
Purpose |
|---|---|---|
GitHubService |
"github" |
Octokit client pool — resolves clients by role (user/agent) or explicit accountId |
Routes
Registered at plugin init on the agent's HTTP server:
| Method | Path | Purpose |
|---|---|---|
GET |
/api/github/token |
Returns { connected, deviceFlowAvailable, username?, scopes?, savedAt? } — token never returned |
POST |
/api/github/token |
Body { token }. Validates against GitHub /user, persists to <state-dir>/credentials/github.json, applies to the live runtime's per-agent settings (setSetting("GITHUB_TOKEN", …, true)) |
DELETE |
/api/github/token |
Clears saved credential (disk + live runtime settings) |
POST |
/api/github/device/start |
Starts a GitHub OAuth device flow (needs the GITHUB_OAUTH_CLIENT_ID setting; 409 with an owner-setup message otherwise). Returns { flowId, userCode, verificationUri, intervalSeconds, expiresInSeconds } — the GitHub device_code never leaves the server |
POST |
/api/github/device/poll |
Body { flowId }. One poll: 200 { status: "pending" | "denied" | "expired" } or, on grant, validates + persists like the PAT route and returns { status: "complete", …connected status }. Flows are scoped to the agent that started them |
Search category
github_pull_requests — registered at init via registerGitHubSearchCategory. Filters: query, repo, state, author, as, accountId, limit. Contexts: code, automation.
Connector account provider
Registers with ConnectorAccountManager at init to expose GitHub accounts (PAT and OAuth) through the generic connector CRUD + OAuth flow surfaces. OAuth requires GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_CLIENT_SECRET, GITHUB_OAUTH_REDIRECT_URI.
Layout
src/
index.ts Plugin export, route wiring, plugin object
register-routes.ts App-route plugin loader — registers githubPlugin via registerAppRoutePluginLoader
types.ts GitHubIdentity, GitHubOctokitClient, GitHubActions, result types
accounts.ts Account config reading (env + character settings + connector store)
action-helpers.ts Shared: service lookup, client resolution, param helpers
rate-limit.ts Rate-limit detection and formatting
github-credentials.ts Local PAT store: load/save/clear at <state-dir>/credentials/github.json
device-flow.ts GitHub OAuth device-flow state machine (start/poll; per-agent scoped; device_code stays server-side)
search-category.ts github_pull_requests search category registration
connector-account-provider.ts ConnectorAccountManager bridge (PAT + OAuth flows)
connector-credential-refs.ts Credential ref persistence helpers
actions/
github.ts GITHUB umbrella action — dispatches by action= param
issue-op.ts GITHUB_ISSUE action (create/assign/close/reopen/comment/label)
pr-op.ts GITHUB_PR action (list/review)
notification-triage.ts GITHUB_NOTIFICATION_TRIAGE action + scoreNotification export
services/
github-service.ts GitHubService — Octokit client pool, account resolution
routes/
github-routes.ts Pure handleGitHubRoutes dispatcher for PAT CRUD endpoints
Commands
bun run --cwd plugins/plugin-github build # tsup ESM build + .d.ts
bun run --cwd plugins/plugin-github test # vitest run
bun run --cwd plugins/plugin-github typecheck # tsc --noEmit
bun run --cwd plugins/plugin-github clean # rm dist .turbo
Config / env vars
| Env var | Required | Purpose |
|---|---|---|
GITHUB_ACCOUNTS |
No (preferred) | JSON array/object of { accountId, role, token } records — supports multiple accounts |
GITHUB_TOKEN |
No | Bootstrap PAT — if already set in the environment, it takes precedence over any locally saved credential; the plugin also writes the saved credential here at startup so that spawned processes (e.g. gh/git) see the same value |
GITHUB_USER_PAT |
No (legacy) | PAT for the user role (acting on behalf of the human) |
GITHUB_AGENT_PAT |
No (legacy) | PAT for the agent role (acting on behalf of the agent) |
GITHUB_USER_ACCOUNT_ID |
No | Override account ID for the legacy user slot (default: "user") |
GITHUB_AGENT_ACCOUNT_ID |
No | Override account ID for the legacy agent slot (default: "agent") |
ELIZA_E2E_GITHUB_USER_PAT |
No | E2E fallback for GITHUB_USER_PAT |
ELIZA_E2E_GITHUB_AGENT_PAT |
No | E2E fallback for GITHUB_AGENT_PAT |
GITHUB_OAUTH_CLIENT_ID |
OAuth only | GitHub OAuth app client ID — also enables the device sign-in path on the settings card (/api/github/device/*) |
GITHUB_OAUTH_CLIENT_SECRET |
OAuth only | GitHub OAuth app client secret |
GITHUB_OAUTH_REDIRECT_URI |
OAuth only | OAuth redirect URI registered on the GitHub app |
At least one account source — GITHUB_ACCOUNTS, GITHUB_USER_PAT, GITHUB_AGENT_PAT, or a character.settings.github.accounts entry — must be set for the plugin's actions to resolve a client. A bare GITHUB_TOKEN is not itself an account source: it bootstraps gh/git subprocess auth (and takes precedence over any locally saved credential) but does not register a user/agent account on its own. A missing user or agent account causes that role's operations to be rejected at runtime (logged as [GitHubService] no GitHub <role> account configured).
Character-level config is also supported under character.settings.github.accounts (array or object keyed by account ID).
How to extend
Add a new action:
- Create
src/actions/my-op.tsexporting a typedActionwithname,contexts,contextGate,roleGate,validate, andhandler. - Use
buildResolvedClient(runtime, selection)fromaction-helpers.tsto get an authenticated Octokit client. - Call
requireConfirmationfrom@elizaos/corefor any write op. - Register the new action in
src/actions/github.tsby extendingGITHUB_ACTIONSand adding a dispatch branch in the umbrella handler, OR add it directly to theactionsarray insrc/index.ts.
Add a new provider/evaluator:
Export from src/index.ts and add to the githubPlugin object's providers or evaluators arrays.
Add a new route:
Add an entry to githubRoutes in src/index.ts and a handler in src/routes/github-routes.ts. Routes use raw http.IncomingMessage / http.ServerResponse — no Express.
Conventions / gotchas
- Confirmation is not optional for write ops. All write actions use
requireConfirmationfrom@elizaos/core. Theconfirmed: trueparameter in the action schema is vestigial — the runtime confirmation gate is authoritative.isConfirmedinaction-helpers.tsalways returnsfalseand is deprecated. - Two identity roles, two PATs.
user= human acting;agent= the Eliza agent acting. Operations that affect the repo on behalf of the agent useagent; operations that respond as the user (reviews, notifications) default touser. - Account precedence:
GITHUB_ACCOUNTSJSON >GITHUB_USER_PAT/GITHUB_AGENT_PATlegacy env vars. Character settings are layered in before env vars. ConnectorAccountManager credentials (OAuth) overlay everything byaccountId. - No test harness for route auth.
handleGitHubRoutesis a pure dispatcher with no auth. The agent's server layer is expected to authenticate before calling the route handler. - Rate limits surface cleanly.
inspectRateLimitinrate-limit.tsdetects GitHub rate-limit responses (HTTP 403 withx-ratelimit-remaining: 0);formatRateLimitMessagerenders a human-readable message with the reset time fromx-ratelimit-reset. - PAT storage is local-first.
<state-dir>/credentials/github.json(mode 0600). Written atomically via a tmp-rename. The token is never returned to the browser via the GET route. GitHubOctokitClientis a structural interface, not the full Octokit class — tests can inject a mock without depending on the real Octokit.tsupbuilds two entry points:src/index.tsandsrc/register-routes.ts.register-routes.tsis an app-route plugin loader that callsregisterAppRoutePluginLoader("@elizaos/plugin-github", ...)— it registers the fullgithubPlugin, it is not a route-only subset.
Verification
Follow the repository-wide verification and evidence standard in the root CLAUDE.md. Run the package's relevant build, typecheck, lint, and test commands, then exercise the real integration boundary changed by the work. Inspect the produced domain artifacts and failure behavior; do not substitute mocked success for the system under test.