GitHub
Trigger AgentField reasoners from signed GitHub webhooks.
The github source verifies GitHub's X-Hub-Signature-256 header with your webhook secret and dispatches the delivery to a reasoner.
Source Surface
| Surface | Value |
|---|---|
| Source name | github |
| Kind | HTTP webhook |
| Secret | Required; GitHub webhook secret from secret_env |
| Config | No per-source config |
| Event type | X-GitHub-Event, plus .action when the payload has an action field |
| Idempotency | X-GitHub-Delivery |
| Reasoner input | The GitHub JSON payload as event, with trigger metadata in _meta |
GitHub does not ship a separate capability node in OSS. The integration surface is the signed webhook source.
Agent Code
from agentfield import Agent, on_event
app = Agent(node_id="repo-agent")
@app.reasoner()
@on_event(
source="github",
types=["pull_request.opened", "pull_request.synchronize", "issues.opened"],
secret_env="GITHUB_WEBHOOK_SECRET",
)
async def handle_github(event: dict, trigger=None):
return {
"event_type": trigger.event_type if trigger else None,
"delivery": trigger.idempotency_key if trigger else None,
"action": event.get("action"),
}
app.run()import { Agent } from '@agentfield/sdk';
const agent = new Agent({ nodeId: 'repo-agent' });
// The github source verifies X-Hub-Signature-256, then dispatches the
// GitHub payload as ctx.input.event with trigger metadata in ctx.input._meta.
// The TypeScript SDK has no declarative source binding — connect the
// `github` source to this reasoner from the UI (or trigger REST API).
agent.reasoner('handleGithub', async (ctx) => {
const { event, _meta } = ctx.input;
return {
event_type: _meta?.event_type ?? null,
delivery: _meta?.idempotency_key ?? null,
action: event?.action,
};
});
agent.serve();a, _ := agent.New(agent.Config{NodeID: "repo-agent", Version: "1.0.0"})
// Declare the github binding in code. The control plane verifies
// X-Hub-Signature-256 with GITHUB_WEBHOOK_SECRET, then dispatches the
// payload as input["event"] with metadata in input["_meta"]. The event name
// is combined with the payload action (e.g. pull_request.opened).
a.RegisterReasoner("handle_github", func(ctx context.Context, input map[string]any) (any, error) {
event, _ := input["event"].(map[string]any)
meta, _ := input["_meta"].(map[string]any)
var eventType, delivery any
if meta != nil {
eventType = meta["event_type"]
delivery = meta["idempotency_key"]
}
return map[string]any{
"event_type": eventType,
"delivery": delivery,
"action": event["action"],
}, nil
},
agent.WithEventTrigger("github", "pull_request.opened", "pull_request.synchronize", "issues.opened"),
agent.WithTriggerSecretEnv("GITHUB_WEBHOOK_SECRET"),
)
a.Serve(context.Background())GitHub Setup
In the repository or organization settings, create a webhook that points at the AgentField trigger URL. Use application/json, set a webhook secret that matches GITHUB_WEBHOOK_SECRET, and choose the event families your reasoner handles.
Common event families:
pull_requestpushissuesissue_commentworkflow_run
When the payload includes an action, AgentField combines it with the GitHub event name. A pull_request webhook with {"action": "opened"} becomes pull_request.opened.
Dispatch Contract
At dispatch time, the target reasoner receives the normalized payload and metadata:
{
"event": {
"action": "opened",
"pull_request": { "number": 42 },
"repository": { "full_name": "Agent-Field/agentfield" }
},
"_meta": {
"source": "github",
"event_type": "pull_request.opened",
"idempotency_key": "<X-GitHub-Delivery>"
}
}X-GitHub-Deliverybecomes the idempotency key.- Signature verification happens before dispatch.
- The GitHub payload is passed through as the normalized
eventbody.