Channels

GitHub

Reach your agent from GitHub App webhooks, with comment invocation, PR diff context, sandbox checkout, and Vercel Connect credentials.

The GitHub channel lets the agent work directly on a repository. Add its invocation token, such as @my-agent, to a new issue, PR, or review comment and the agent answers in that thread, with the PR diff already in context and the repo checked out into the sandbox. The token is an eve convention: GitHub may not autocomplete it or render it as a linked mention. The channel takes GitHub App webhooks at /eve/v1/github, checks the signature, derives auth from whoever triggered the event, and replies on the native surface. Credentials can run through Vercel Connect, which manages the GitHub App, the installation token, and inbound webhook verification, so there's no app private key or webhook secret for you to hold. See Channels for the contract this builds on.

Guided Connect setup

Run the registry setup from the agent directory:

eve add channel/github

The flow signs you in to Vercel when needed, creates or links a Vercel project, provisions an app-scoped GitHub Connect client, and registers /eve/v1/github as a trigger destination. It then installs @vercel/connect and writes agent/channels/github.ts with the connector UID.

Vercel Connect creates the GitHub App, receives and verifies its webhooks, and forwards them to the deployed agent. After deploying, open the GitHub App in the Connect dashboard and install it in the organization or account where you want to use it. Add the generated invocation token (for example, @my-agent) to a new issue, pull request, or review comment to start a conversation. GitHub may not autocomplete the token or render it as a linked mention.

The generated channel uses Connect-managed credentials:

agent/channels/github.ts
import { connectGitHubCredentials } from "@vercel/connect/eve";
import { githubChannel } from "eve/channels/github";

export default githubChannel({
  botName: "my-agent",
  credentials: connectGitHubCredentials("github/my-agent"),
});

connectGitHubCredentials returns { installationToken, webhookVerifier }: eve uses the Connect-managed installation token directly for GitHub API calls, skipping its native App JWT exchange, and verifies Connect-forwarded webhooks by their Vercel OIDC signature instead of a GitHub webhook secret. Token rotation, refresh, and multi-installation tenancy stay inside Connect, so there is no GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, or GITHUB_WEBHOOK_SECRET to manage.

Bring your own GitHub App

To run a GitHub App you manage yourself, pass its credentials directly instead:

agent/channels/github.ts
import { githubChannel } from "eve/channels/github";

export default githubChannel({
  botName: "my-agent",
  credentials: {
    appId: process.env.GITHUB_APP_ID,
    privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
    webhookSecret: process.env.GITHUB_WEBHOOK_SECRET,
  },
});

Every field falls back to an env var, so you can drop the credentials block entirely once these are set:

GITHUB_APP_ID=...            # GitHub App id
GITHUB_APP_PRIVATE_KEY=...   # GitHub App private key (PEM)
GITHUB_WEBHOOK_SECRET=...    # verifies the webhook signature
GITHUB_APP_SLUG=...          # supplies botName when it is not set in config

appId/privateKey/webhookSecret also take a lazy resolver function if you'd rather fetch them on demand, and so does botName: it resolves on first use inside request handling, caches on success, and retries on the next event after a failure, so a resolver that depends on request-scoped credentials works in production. When botName is not configured, the channel falls back to the credentials' appSlug, then to GITHUB_APP_SLUG.

Point the GitHub App webhook URL at https://<deployment>/eve/v1/github. For comment-invoked turns, subscribe to issue_comment and pull_request_review_comment; add issues, pull_request, check_suite, check_run, or workflow_run if you wire up their opt-in hooks. After installing the App for the repository, a new comment that includes @botName starts a turn. This is a text invocation token, not a GitHub-native mention: GitHub may display the App as botName[bot], but it may not autocomplete or link @botName.

How the channel handles messages

Dispatch

Inbound hooks return { auth } to dispatch, or null to ignore. Return title alongside auth to set the title when the dispatch starts a run. Use defaultGitHubAuth(ctx) to derive auth from the actor.

import { defaultGitHubAuth, githubChannel } from "eve/channels/github";

export default githubChannel({
  botName: "my-agent",
  // Replaces the default invocation-token gate. ctx.conversation.kind is "issue", "pull_request", or "review_thread".
  onComment: (ctx, comment) => ({ auth: defaultGitHubAuth(ctx) }),
  // Opt in; no default dispatch on these events.
  onIssue: (ctx, issue) => (issue.action === "opened" ? { auth: defaultGitHubAuth(ctx) } : null),
  onPullRequest: (ctx, pr) => (pr.action === "opened" ? { auth: defaultGitHubAuth(ctx) } : null),
  onCheckSuite: (ctx, suite) =>
    suite.action === "completed" &&
    suite.conclusion === "failure" &&
    suite.app.slug === "github-actions" &&
    suite.pullRequests.length > 0
      ? {
          auth: defaultGitHubAuth(ctx),
          context: [`Triage failed check suite ${suite.checkSuiteId} at ${suite.headSha}.`],
        }
      : null,
});

The CI hooks expose normalized action, status, conclusion, app.slug, headSha, and pullRequests fields, plus checkSuiteId, checkRunId, or workflowRunId. workflow_run is a GitHub Actions-only event, so its normalized app.slug is "github-actions". A dispatched CI turn is anchored to the first number in pullRequests; the hook still runs when the array is empty, but it must return null because there is no issue or PR thread for the session.

Delivery

When a turn starts, the channel adds an eyes reaction to the triggering comment (turn this off with progress: { reactions: false }). The reply comes back as a comment, on the timeline or in the review thread, and splits across multiple comments when it runs long. If the turn fails, you get a short error comment carrying an error id.

Human-in-the-loop (HITL)

GitHub comments have no interactive button or card affordance. A human-in-the-loop (HITL) input.requested event is posted as a comment prompt, and the user's reply comment maps back to the pending input request. Declare an events["input.requested"] handler to customize the prompt.

Proactive sessions

Start a session without an inbound comment invocation through to(github, target).send(message, { auth }) from a schedule run handler, or ctx.to(github, target).send(message, { auth }) from another channel. The target requires owner, repo, and exactly one of issueNumber or pullRequestNumber.

Attachments

Inbound file attachments are not supported on this channel today. Repository contents reach the agent through the sandbox checkout below, not as message attachments.

PR context

Summon the agent on a PR and it always sees the diff. PR metadata and the changed-file patch land in context. Large generated files still appear in the list, but their patch body is dropped; add more paths to the skip list with pullRequestContext.excludedFiles.

Sandbox checkout

Before the first model call, every triggered turn checks out the relevant ref into the sandbox, so read_file/glob/grep/bash all run against the real tree. The installation token never enters the sandbox. git fetches a token-free URL, and the platform injects auth on egress at the firewall. That requires a firewall-capable backend (Vercel); the local backend skips checkout. Within a session, checkout is incremental across turns.

Arbitrary API calls

For anything the channel doesn't wrap, call ctx.github.request({ method, path, body }). It carries installation-token auth.