@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
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)acceptssession.stream(prompt)for one turn, orsession.events()to observe a session.{ signal: request.signal }cancels the Devin turn as soon as the client stops (useChat’sstop()). Without it, cancelling the stream cancels the turn when Devin’s next update arrives.devinSessionMetadata(session)puts{ sessionId, sessionUrl }on the message, plus thestopReasonwhen 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
ForuseChat’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.
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.
