{"slug": "add-resumable-streaming-and-reliable-tool-calling-to-your-openai-agent", "title": "Add resumable streaming and reliable tool calling to your OpenAI agent", "summary": "Ably's AI Transport SDK v0.7.0 adds integration with OpenAI's Responses API, enabling resumable streaming and reliable tool calling for agent-to-user conversations, while v0.8.0 supports Vercel AI SDK 7. The new ResponsesCodec maps OpenAI's Responses event stream onto AI Transport sessions, allowing multi-device access and approval gates without extra infrastructure.", "body_md": "## New in AI Transport: integration with OpenAI's Responses API and support for Vercel AI SDK 7\n\nIf you build an agent against OpenAI's Responses API then the simplest way to get output to the user is streaming over HTTP/SSE. If the user refreshes the page, loses connection, switches devices, or needs to approve a tool call, then there's nothing in the API to help you.\n\nAI Transport is Ably's session layer for agent-to-user conversations. An agent built on it gets resumable streams, multi-device sessions, and approval gates that wait for a human user, without deploying additional infrastructure.\n\nIntegration with OpenAI Responses API is available from AI Transport SDK v0.7.0. To get started, use the new `ResponsesCodec`\n\n, which carries an OpenAI Responses event stream over an AI Transport session. The codec maps the Responses event stream onto AI Transport messages. The response is then accessible on the session to any number of clients, which can read it, leave, and rejoin. It ships in a new entry point, `@ably/ai-transport/openai`\n\n.\n\nFor those building with Vercel, AI Transport v0.8.0 is updated to support Vercel AI SDK 7. The `ai`\n\npeer dependency range widens to `^6 || ^7. ChatTransport`\n\n, the codec's public types, and the wire format are unchanged, so v6 will continue working.\n\n## What a codec provides\n\nA codec transforms AI model events into messages on an Ably channel. This allows the transport to handle runs, branching, hydration, and control signals without knowing the internal structure of an AI message.\n\n`ResponsesCodec`\n\ntakes OpenAI's own `Responses.ResponseStreamEvent`\n\ndirectly from the model output. Pass the codec when you build the agent session that handles the run:\n\n``` js\nimport { createAgentSession } from '@ably/ai-transport';\nimport { ResponsesCodec } from '@ably/ai-transport/openai';\n\nconst session = createAgentSession({\n  client: ably,\n  channelName: invocation.sessionName,\n  codec: ResponsesCodec,\n});\n```\n\nPass the same codec when constructing a client session in the browser. The browser then reads the conversation and decodes it with the OpenAI codec, giving you the message tree, branching, and pagination.\n\n## Pipe the Responses stream onto the session\n\n`ResponsesCodec`\n\nencodes the Responses event union directly, along with two events of its own. An event that comes off the model's response stream can be piped directly into the session, with no adapting step in between.\n\n``` js\nawait session.connect();\nconst run = session.createRun(invocation, {}, { signal: req.signal });\n\n// Drain history so the run can see its own triggering input, then open it.\nwhile (run.view.hasOlder()) await run.view.loadOlder();\nawait run.start();\n\nconst input = toResponsesInput(run.view.getMessages().map(({ message }) => message));\n\nconst stream = await openai.responses.create(\n  { model: 'gpt-5.5', input, stream: true },\n  { signal: run.abortSignal },\n);\nconst { reason } = await run.pipe(stream);\nawait run.end({ reason });\n```\n\n`run.pipe`\n\nreads the stream, encodes each event, and publishes it. It also watches `run.abortSignal`\n\n. When you press stop on any device, the run ends and the model's inference request is cancelled. Confirmation of the cancellation is then published back to the session.\n\n## Client-run tools and approval gates\n\nThe Responses API has events representing a request for you to execute a `function_call`\n\n: the model emits calls, you run them, and the outputs go back as input on the next model request. The model doesn't care if these function calls run on the agent or in the browser.\n\nThe Responses API doesn't have a native event to represent a tool's output, and it has none for a tool approval, so the codec adds `function_call_output`\n\nand `tool-approval-request`\n\n. `function_call_output`\n\ncarries the output of a tool, published by whichever participant ran the tool. `tool-approval-request`\n\ncarries the call id, the tool name, and the arguments a client needs to render the prompt.\n\nOpenAI's items have no field for an approval decision or a failed result, so the codec records those statuses alongside the message. `toResponsesInput`\n\nfilters the messages to just those that should be sent to the next LLM inference call.\n\nTo publish the output of a `function_call`\n\n, use one of the following patterns:\n\n``` js\nimport { ResponsesCodec } from '@ably/ai-transport/openai';\n\n// A client-run tool succeeded.\nawait view.send(ResponsesCodec.createToolResult(id, { call_id, output }), { runId });\n\n// A client-run tool failed. The message becomes the output the model reads next turn.\nawait view.send(ResponsesCodec.createToolResultError(id, { call_id, message }), { runId });\n\n// A person approved or denied a gated tool.\nawait view.send(ResponsesCodec.createToolApprovalResponse(id, { call_id, approved }), { runId });\n```\n\nOnce the tool output is published, invoke the agent to process that new input. The client publishes the tool response, then POSTs to your agent endpoint to wake it. The agent rebuilds the conversation from the session.\n\nThe SDK gives you helpers for matching calls to their outputs and for finding the approvals an agent needs before continuing:\n\n``` js\nimport { approvedUnexecutedCalls, unansweredCalls } from '@ably/ai-transport/openai';\n\n// Agent, resuming a suspended run.\nconst messages = run.view.getMessages().map(({ message }) => message);\n\n// The provider rejects the turn if any open call still lacks an output.\nif (unansweredCalls(messages).length > 0) return;\n\n// An approval is a decision, not an output, so the agent owes these calls a run.\nconst approved = approvedUnexecutedCalls(messages);\n```\n\nA model can only continue its inference once all `function_call`\n\nrequests have been answered. These helpers tell you what state each call is in:\n\n`unansweredCalls`\n\ntells you which calls are still waiting for a response. A call counts as answered once it has an output, and also once it has been approved or denied, because the agent runs an approved call on resume.`approvedUnexecutedCalls`\n\nsurfaces to the agent which tool calls are approved but are waiting to be executed.`resolvedCallIds`\n\ngives the UI the calls that already have an output, so a renderer can show the answer attached to its call.\n\n## Get started\n\nInstall the SDK:\n\n```\nnpm install ably @ably/ai-transport openai\n```\n\n`openai`\n\nis an optional peer dependency, needed only for this entry point. Node 22 or newer.\n\n[Get started with OpenAI](https://ably.com/docs/ai-transport/getting-started/openai): build the app in this post end to end.[OpenAI Responses](https://ably.com/docs/ai-transport/frameworks/openai): the codec,`toResponsesInput`\n\n, and the server-side tool loop.[Read the source](https://github.com/ably/ably-ai-transport-js), including the[runnable OpenAI demo](https://github.com/ably/ably-ai-transport-js/tree/main/demo/openai/react/use-client-session)with client-side tools and approval gates.[Read the release notes](https://github.com/ably/ably-ai-transport-js/releases/tag/0.8.0)for the full change list, including this release's breaking changes.[Sign up free](https://ably.com/sign-up): you need an Ably account and an API key. The free tier includes 6M messages a month, no card required.", "url": "https://wpnews.pro/news/add-resumable-streaming-and-reliable-tool-calling-to-your-openai-agent", "canonical_source": "https://ably.com/blog/ai-transport-openai-responses-api", "published_at": "2026-09-02 15:02:05+00:00", "updated_at": "2026-09-02 15:25:46.519617+00:00", "lang": "en", "topics": ["ai-infrastructure", "developer-tools", "ai-agents"], "entities": ["Ably", "OpenAI", "Responses API", "AI Transport SDK", "ResponsesCodec", "Vercel AI SDK 7"], "alternates": {"html": "https://wpnews.pro/news/add-resumable-streaming-and-reliable-tool-calling-to-your-openai-agent", "markdown": "https://wpnews.pro/news/add-resumable-streaming-and-reliable-tool-calling-to-your-openai-agent.md", "text": "https://wpnews.pro/news/add-resumable-streaming-and-reliable-tool-calling-to-your-openai-agent.txt", "jsonld": "https://wpnews.pro/news/add-resumable-streaming-and-reliable-tool-calling-to-your-openai-agent.jsonld"}}