Skip to main content
@cognition-ai/ai-sdk turns the typed events of a @cognition-ai/sdk session into AI SDK UI message streams, so a Devin cloud session can back useChat from any server route — Node.js, Cloudflare Workers or Deno. Devin’s messages and thoughts become text and reasoning parts, its tool calls become dynamic-tool parts, and its plans and pull requests become data parts your UI can render. This is the package for a chat you host yourself, talking to Devin’s cloud. For Devin running inside a sandbox you own, see the AI SDK harness; for a ready-made agent on Cloudflare, see Cloudflare Agents, which is built on this package.

Setup

The @cognition-ai packages are in beta. Every release is a 0.0.1-beta.N prerelease on the beta dist-tag (currently 0.0.1-beta.7), so the @beta suffix is required: a bare npm install @cognition-ai/sdk fails on purpose. Pin the exact version in your lockfile, and keep every @cognition-ai package you use (sdk, ai-sdk, cloudflare-agents, harness-devin) on the same version; they release together. Each published version is stable, but the API can change between beta bumps; see the changelog for what moved.

A chat route

app/api/chat/route.ts
On the client, useChat<DevinUIMessage>() types the Devin data parts.
  • toPromptInput(messages) sends only the latest user message. The Devin session keeps its own transcript, so there is nothing to replay.
  • toUIMessageStream(events) accepts session.stream(prompt) for one turn, or session.events() to observe a session.
  • { signal: request.signal } cancels the Devin turn as soon as the client stops (useChat’s stop()). Without it, cancelling the stream cancels the turn when Devin’s next update arrives.
  • devinSessionMetadata(session) puts { sessionId, sessionUrl } on the message, plus the stopReason when the turn finishes.

What the stream contains

A tool part’s input is the SDK’s normalized ToolCallDetail (exec with command, cwd, exitCode; edit with files; mcp with server and name; …), and its output is { detail, text, rawOutput }, so a UI can render a bash card with the command and its exit code instead of raw JSON. UI message chunks only append, so a Devin message that is revised without extending its text starts a new part.

Reloading history

toUIMessages(replay) rebuilds the chat after a page reload, using the same rules as the stream. Status, activity and lifecycle parts are transient, so they are not in the result.

Reconnecting to a running turn

For useChat’s resumeStream(), serve a GET route that observes the session. endWhenIdle ends the stream when the turn goes idle; session.events() otherwise never ends.
app/api/chat/[id]/stream/route.ts

Approving tool calls

ToolApprovals turns Devin permission requests into AI SDK tool approvals. The stream sends a tool-approval-request and finishes; the user answers with addToolApprovalResponse, and the next request resumes the same Devin turn. Keep one ToolApprovals per session in something that outlives a request, such as a Durable Object.
On the client, set sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses so answering sends the request. Approving picks Devin’s allow-once option and denying picks reject-once (a denied tool shows as output-denied).
Devin’s cloud also reports permission decisions it needs as permission_request tool calls in the transcript (detail.tool === "permission_request", with a requestId), answered with session.respondToPermission(). A UI that renders tool parts can offer Allow / Deny on those directly; the Cloudflare Agents chat example does both.

Where it runs

Anywhere @cognition-ai/sdk connects to the cloud: Node.js, Bun, Cloudflare Workers, Deno 2.5+. See Where it runs for the runtime rules.