Frameworks
Run GitHub tools as durable, retryable steps with createDurableGithubAgent, crash-safe agents for Nuxt and Next.js apps on Vercel.

Long-running assistants and chat backends often need durable execution: if a function restarts, the same logical run should resume, tool calls should retry safely, and each step should be observable. @github-tools/sdk supports this through the Vercel Workflow SDK and the @github-tools/sdk/workflow entry point.

Integrate a durable GitHub assistant

What “durable” means here

  • "use workflow": Your orchestration function is a workflow: the platform can pause, resume, and replay it across failures and deploys.
  • "use step": Each GitHub tool implementation runs inside a named, module-level step. Every tool call is a durable step with retries and full Node.js access in the workflow runtime.
  • createDurableGithubAgent: Wraps the same GitHub tools in a WorkflowAgent from @ai-sdk/workflow so LLM turns and tool invocations participate in that durable model (not just raw generateText in a plain serverless handler).

Together, this is the recommended pattern when you build Nuxt, Next.js, or other apps that expose a chat or agent API and must not lose progress on timeout or cold start.

Install optional workflow dependencies

Durable agents are optional. Add them only when you use @github-tools/sdk/workflow:

pnpm add workflow @ai-sdk/workflow

You still need ai, zod, and @github-tools/sdk as documented in Installation.

Minimal durable workflow

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)

durable-chat.workflow.ts
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
import { getWritable } from 'workflow'
import type { ModelCallStreamPart, ModelMessage } from 'ai'

export async function durableGithubChat(
  messages: ModelMessage[],
  token: string,
  model: string
) {
  'use workflow'

  const writable = getWritable<ModelCallStreamPart>()

  const agent = createDurableGithubAgent({
    model,
    token,
    preset: 'code-review',
    requireApproval: true,
  })

  await agent.stream({ messages, writable })
}

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:

review-agent.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 it into your framework

Workflows are plain exported functions. Your framework's Workflow integration starts the run from an API route and streams results to the client.

Place workflows under server/workflows/ and start them from a Nitro server route:

server/api/chat.post.ts
import { start } from 'workflow/api'
import { durableGithubChat } from '../workflows/durable-chat.workflow'

export default defineEventHandler(async (event) => {
  const { messages, model } = await readBody(event)
  const session = await requireUserSession(event)

  const run = await start(durableGithubChat, [messages, session.secure.githubToken, model])

  setHeader(event, 'x-workflow-run-id', run.runId)
  return run.readable
})
For a complete working example that connects a durable agent to GitHub via Chat SDK, see the examples/pr-review-agent/ starter: a ~60-line PR review agent with multi-turn durable sessions and evlog AI observability. Full walkthrough: Chat SDK.

Presets and options

All presets (code-review, issue-triage, repo-explorer, ci-ops, security-audit, release-manager, maintainer) work with createDurableGithubAgent. Options mirror createGithubAgent for model, token, preset, instructions, and temperature, with additional pass-through for WorkflowAgentOptions fields like experimental_telemetry, onStepEnd, onEnd, and prepareStep. Use stopWhen (for example stepCountIs(n)) instead of the deprecated maxSteps parameter.

Approval control and durable agents

requireApproval maps to needsApproval on write tools. When a tool needs approval, WorkflowAgent pauses the workflow, emits an approval request to the stream, and resumes when the user approves or denies: the same UX as createGithubAgent, but durable across restarts.

Wire the client with WorkflowChatTransport and a GET reconnect route ({api}/{runId}/stream) so long runs survive timeouts. The demo chat app toggles this with the shield icon.

For richer approval policies ('once' per session, input-dependent predicates), use the eve extension.

Standard agents vs durable agents

createGithubAgentcreateDurableGithubAgent
Import@github-tools/sdk@github-tools/sdk/workflow
RuntimeIn-process ToolLoopAgentDurableGithubAgent inside "use workflow"
Methods.generate(), .stream().generate(), .stream()
Retries / resumeYou handleWorkflow-managed durable steps
requireApprovalSupportedSupported (via needsApproval on tools)

Durable steps on every tool

Even if you do not use createDurableGithubAgent, spreading createGithubTools() inside a workflow still benefits from per-tool "use step" boundaries when the AI SDK executes tools. Each GitHub operation remains a proper workflow step. The durable agent entry point additionally durably wraps the LLM loop itself.

External references