Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/durable-agent-generate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@github-tools/sdk": minor
---

Add `.generate()` to `DurableGithubAgent` for non-streaming durable workflows

`createDurableGithubAgent` now returns a `DurableGithubAgent` wrapper with both `.stream()` and `.generate()` methods. `.generate()` uses `generateText` from the AI SDK internally and must be called from a `"use step"` context in workflows.

`CreateDurableGithubAgentOptions` now extends all `DurableAgentOptions` fields (e.g. `experimental_telemetry`, `onStepFinish`, `onFinish`, `prepareStep`) instead of a narrow subset, enabling evlog and other observability integrations at the agent level.
6 changes: 3 additions & 3 deletions apps/chat/server/workflows/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ export async function durableChatWorkflow(
const agent = createDurableGithubAgent({
model,
token,
additionalInstructions,
maxSteps
additionalInstructions
})

await agent.stream({
messages,
writable
writable,
maxSteps
})
}
2 changes: 1 addition & 1 deletion apps/docs/content/docs/1.getting-started/1.introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ See [Examples](/guide/examples) for more agent patterns.

For chat apps and agents that must **survive restarts and retries**, use **durable** execution via the Vercel Workflow SDK. Import `createDurableGithubAgent` from `@github-tools/sdk/workflow` inside a function marked with `"use workflow"`. Each GitHub tool already runs as a durable `"use step"`, and the durable agent makes the LLM loop itself step-aware.

Read the full walkthrough in [Durable workflows (Vercel Workflow)](/guide/durable-workflows).
Read the full walkthrough in [Durable workflows (Vercel Workflow)](/guide/durable-workflows), or see the [`examples/pr-review-bot/`](https://github.com/vercel-labs/github-tools/tree/main/examples/pr-review-bot) starter for a complete PR review bot connected to GitHub via [Chat SDK](https://chat-sdk.dev).

## Agent Skills

Expand Down
36 changes: 36 additions & 0 deletions apps/docs/content/docs/2.guide/6.examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,41 @@ export async function streamDurableGithubChat(

`requireApproval` is not enforced by `DurableAgent` today — plan access control in your route or use `createGithubAgent` when you need approval UX. More detail: [Durable workflows (Vercel Workflow)](/guide/durable-workflows).

## GitHub bot (Chat SDK + Workflow)

A complete, durable PR review bot connected to GitHub via [Chat SDK](https://chat-sdk.dev). When someone @mentions the bot on a PR, a durable workflow runs the `code-review` agent — reading files, commits, and diffs using GitHub tools — then posts the review as a comment. Follow-up messages resume the same workflow.

The full example lives in [`examples/pr-review-bot/`](https://github.com/vercel-labs/github-tools/tree/main/examples/pr-review-bot). It combines four primitives in ~60 lines of code:

- **`@github-tools/sdk`** — `createGithubTools` with the `code-review` preset
- **[Chat SDK](https://chat-sdk.dev)** — `@chat-adapter/github` for webhook handling and thread management
- **[Vercel Workflow](https://vercel.com/docs/workflow)** — `"use workflow"` + `createHook` for durable, multi-turn sessions
- **[evlog](https://evlog.dev)** — `createAILogger` for AI observability (tokens, tool calls, cost)

```ts [review.ts (workflow)]
import { createGithubTools } from '@github-tools/sdk'
import { generateText } from 'ai'
import { createLogger } from 'evlog'
import { createAILogger } from 'evlog/ai'

async function runAgentTurn(prompt: string) {
'use step'

const log = createLogger()
const ai = createAILogger(log, { toolInputs: { maxLength: 500 } })

const result = await generateText({
model: ai.wrap('anthropic/claude-sonnet-4.6'),
tools: createGithubTools({ preset: 'code-review' }),
prompt,
maxSteps: 15,
})

log.emit()
return result.text
}
```

## Choose the right pattern

| Pattern | Entry point | Best for |
Expand All @@ -211,5 +246,6 @@ export async function streamDurableGithubChat(
| Streaming | `streamText` + `createGithubTools` | chat UIs, interactive terminals |
| Reusable agent | `createGithubAgent` | multi-turn assistants, persistent bots |
| **Durable** agent + streaming | `createDurableGithubAgent` + `"use workflow"` | hosted chat, crash-safe tool loops, Nuxt/Next APIs on Vercel |
| **Platform bot** | `createGithubTools` + Chat SDK + Workflow | GitHub/Slack/Discord bots with durable multi-turn sessions |

See [API Reference](/api/reference) for the full type signatures and [Tools Catalog](/api/tools-catalog) for every available tool.
44 changes: 40 additions & 4 deletions apps/docs/content/docs/2.guide/7.durable-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ You still need `ai`, `zod`, and `@github-tools/sdk` as documented in [Installati

## Minimal durable workflow

Define a workflow function with `"use workflow"`, create a **durable GitHub agent**, and stream UI chunks to a writable sink:
`createDurableGithubAgent` returns a `DurableGithubAgent` with two methods:

- **`.stream()`** — real-time output to a `WritableStream` (for chat UIs)
- **`.generate()`** — non-streaming, returns the full text response (for bots, background jobs, webhooks)

Both methods execute each tool call as a durable workflow step with automatic retries.

### Streaming (chat UI)

```ts [durable-chat.workflow.ts]
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
Expand All @@ -79,7 +86,35 @@ export async function durableGithubChat(
}
```

Wire this workflow from an API route or server handler provided by your framework’s Workflow integration (for example a Nuxt server route that starts the run and streams results to the client).
### Non-streaming (bot / background job)

For non-streaming use cases (bots, webhooks, background jobs), use `createGithubAgent` inside a `"use step"` function. This gives you the full tool loop while keeping the step durable:

```ts [review-bot.workflow.ts]
import { createGithubAgent } from '@github-tools/sdk'

async function runAgentTurn(prompt: string) {
'use step'
const agent = createGithubAgent({
model: 'anthropic/claude-sonnet-4.6',
preset: 'code-review',
requireApproval: false,
})
const { text } = await agent.generate({ prompt })
return text
}

export async function reviewWorkflow(prompt: string) {
'use workflow'
await runAgentTurn(prompt)
}
```

Wire these workflows from an API route or server handler provided by your framework’s Workflow integration (for example a Nuxt server route that starts the run and streams results to the client).

::note
For a complete working example that connects a durable agent to GitHub via [Chat SDK](https://chat-sdk.dev), see the [`examples/pr-review-bot/`](https://github.com/vercel-labs/github-tools/tree/main/examples/pr-review-bot) starter — a ~60-line PR review bot with multi-turn durable sessions and [evlog](https://evlog.dev) AI observability.
::

## AI assistant prompt (durable workflow)

Expand All @@ -94,7 +129,7 @@ Integrate a durable GitHub assistant using @github-tools/sdk/workflow and the Ve

## Presets and options

All presets (`code-review`, `issue-triage`, `repo-explorer`, `ci-ops`, `maintainer`) work with `createDurableGithubAgent`. Options mirror [`createGithubAgent`](/api/reference) for model, token, preset, instructions, `maxSteps`, and temperature.
All presets (`code-review`, `issue-triage`, `repo-explorer`, `ci-ops`, `maintainer`) work with `createDurableGithubAgent`. Options mirror [`createGithubAgent`](/api/reference) for model, token, preset, instructions, and temperature, with additional pass-through for `DurableAgentOptions` fields like `experimental_telemetry`, `onStepFinish`, `onFinish`, and `prepareStep`.

## Approval control and durable agents

Expand All @@ -109,7 +144,8 @@ When you need human-in-the-loop approvals on writes, use [`createGithubAgent`](/
| | `createGithubAgent` | `createDurableGithubAgent` |
|---|---|---|
| Import | `@github-tools/sdk` | `@github-tools/sdk/workflow` |
| Runtime | In-process `ToolLoopAgent` | `DurableAgent` inside `"use workflow"` |
| Runtime | In-process `ToolLoopAgent` | `DurableGithubAgent` inside `"use workflow"` |
| Methods | `.generate()`, `.stream()` | `.generate()`, `.stream()` |
| Retries / resume | You handle | Workflow-managed durable steps |
| `requireApproval` | Supported | Ignored today (see above) |

Expand Down
8 changes: 8 additions & 0 deletions examples/pr-review-bot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# GitHub Personal Access Token (repo scope)
GITHUB_TOKEN=ghp_...

# Webhook secret (must match your GitHub webhook config)
GITHUB_WEBHOOK_SECRET=...

# Bot username for @mention detection
GITHUB_BOT_USERNAME=my-review-bot
3 changes: 3 additions & 0 deletions examples/pr-review-bot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
.output
.env
168 changes: 168 additions & 0 deletions examples/pr-review-bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# GitHub PR Review Bot

A durable PR review bot in **~60 lines of code**. Tag it on any pull request, and it analyzes the changes, posts a structured review, and responds to follow-ups — all crash-safe.

Built with:

- **[@github-tools/sdk](https://github-tools.com)** — 36 AI-callable GitHub tools (PRs, commits, issues, code search...)
- **[Chat SDK](https://chat-sdk.dev)** — multi-platform bot framework with the GitHub adapter
- **[Vercel Workflow](https://useworkflow.dev)** — durable execution that survives timeouts and restarts
- **[evlog](https://evlog.dev)** — AI observability (token usage, tool calls, cost, timing)

## How it works

```
@my-bot review this PR
Chat SDK receives webhook
👀 reaction added ──► starts durable workflow
Agent reads PR (files, diffs, commits)
Posts structured review comment
Listens for follow-up messages
```

Each tool call is a **durable step** — if the server crashes mid-review, the workflow resumes from the last completed step.

## Setup

### 1. Environment variables

Copy `.env.example` to `.env`:

```bash
# GitHub PAT with repo scope (or fine-grained with Issues + PRs read/write)
GITHUB_TOKEN=ghp_...

# Must match your GitHub webhook config
GITHUB_WEBHOOK_SECRET=...

# Bot username — this is how @mentions are detected
GITHUB_BOT_USERNAME=my-review-bot
```

> **Important**: use a different GitHub account for the bot than the one commenting. The Chat SDK filters self-messages to prevent loops.

### 2. Configure a GitHub webhook

1. Go to your repository **Settings > Webhooks > Add webhook**
2. **Payload URL**: `https://your-domain.com/webhooks/github` (or tunnel URL for local dev)
3. **Content type**: `application/json`
4. **Secret**: same value as `GITHUB_WEBHOOK_SECRET`
5. **Events**: select _Issue comments_ and _Pull request review comments_

### 3. Install and run

```bash
pnpm install
pnpm dev
```

For local development, expose your server with a tunnel:

```bash
# Using cloudflared (recommended)
cloudflared tunnel --url http://localhost:3000

# Or ngrok
ngrok http 3000
```

Then update the webhook URL in your GitHub repository settings.

### 4. Try it

Open a PR and comment `@my-review-bot review this PR`.

## Project structure

```
server/
lib/bot.ts # Chat instance + event handlers
routes/webhooks/github.post.ts # Webhook endpoint (5 lines)
workflows/review.ts # Durable agent workflow
review-template.md # Review output format
```

## Customizing

### Review format

Edit `review-template.md` to change how the bot formats its reviews. The template is injected into the agent's instructions.

### Preset

The bot uses the `code-review` preset which includes tools for reading PRs, files, commits, and posting reviews. Swap it for any preset:

| Preset | Tools included | Use case |
|--------|---------------|----------|
| `code-review` | PR files, diffs, blame, review comments | PR review bots |
| `issue-triage` | Issues, labels, comments | Issue management |
| `maintainer` | All read + write tools | Full repo management |
| `ci-ops` | Workflow runs, jobs, dispatch | CI/CD monitoring |
| `repo-explorer` | All read-only tools | Code exploration |

```ts
const agent = createGithubAgent({
model: 'anthropic/claude-sonnet-4.6',
preset: 'maintainer', // ← change preset
requireApproval: false,
})
```

### Model

Replace the model string with any [AI Gateway](https://vercel.com/docs/ai-gateway) model:

```ts
model: 'openai/gpt-4.1'
model: 'google/gemini-2.5-pro'
model: 'anthropic/claude-sonnet-4.6'
```

### Adding platforms

The bot works on GitHub out of the box, but Chat SDK supports multiple platforms. Add adapters to `bot.ts`:

```ts
import { createSlackAdapter } from '@chat-adapter/slack'

const adapters = {
github: createGitHubAdapter(),
slack: createSlackAdapter(),
}
```

Your handlers (`onNewMention`, `onSubscribedMessage`) work across all platforms automatically. Add a webhook route for each platform.

### Production state

Replace in-memory state with Redis for persistent thread subscriptions across restarts:

```ts
import { createRedisState } from '@chat-adapter/state-redis'

const bot = new Chat({
// ...
state: createRedisState(),
})
```

### Observability

The bot logs every agent turn with [evlog](https://evlog.dev), including:

- Model, provider, and call count
- Token usage and estimated cost
- Tool calls with timing and inputs
- Step-by-step breakdown

Logs appear in the terminal by default. Add a [drain](https://evlog.dev/drains) to forward them to Axiom, Datadog, or any OTLP-compatible service.
6 changes: 6 additions & 0 deletions examples/pr-review-bot/nitro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'nitro'

export default defineConfig({
serverDir: './server',
modules: ['workflow/nitro'],
})
26 changes: 26 additions & 0 deletions examples/pr-review-bot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@github-tools/pr-review-bot",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"workflow:web": "npx workflow web"
},
"dependencies": {
"@github-tools/sdk": "workspace:*",
"ai": "^6",
"chat": "latest",
"@chat-adapter/github": "latest",
"@chat-adapter/state-memory": "latest",
"evlog": "latest",
"zod": "^4",
"workflow": "latest",
"@workflow/ai": "latest"
},
"devDependencies": {
"nitro": "latest",
"typescript": "^5.8",
"vite": "^7"
}
}
Loading
Loading