# vercelRunOutcome `vercelRunOutcome` resolves the outcome of a Vercel `streamText` response that was piped through [`Run.pipe`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#pipe). It returns either a terminal [`RunEndReason`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-end-reason) you pass to [`Run.end`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-end), or the sentinel `'suspend'` telling you to call [`Run.suspend`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-suspend) instead. It preserves transport-level outcomes (`'cancelled'`, `'error'`) from the pipe result. When the pipe completed naturally, it awaits Vercel's `finishReason` and returns `'suspend'` for `'tool-calls'` (the LLM requested tools the SDK did not auto-execute, so the run should pause for the next client-published tool result), or `'complete'` otherwise. Use it at the end of every Vercel route handler so the lifecycle event you publish matches what actually happened on the LLM side. #### Javascript ``` import { streamText } from 'ai'; import { vercelRunOutcome } from '@ably/ai-transport/vercel'; const result = streamText({ model, messages, tools }); const pipeResult = await run.pipe(result.toUIMessageStream()); const outcome = await vercelRunOutcome(pipeResult, result.finishReason); if (outcome === 'suspend') { await run.suspend(); } else { await run.end(outcome); } ``` ## Resolve the run outcome `vercelRunOutcome(pipeResult: StreamResult, finishReason: PromiseLike): Promise` The helper applies two rules: 1. If `pipeResult.reason` is `'cancelled'` or `'error'`, return it as-is. The transport already knows the run ended for a non-completion reason. 2. If `pipeResult.reason` is `'complete'`, await `finishReason` and translate: - `'tool-calls'` returns `'suspend'`. The LLM requested tools the SDK did not auto-execute, so the run should pause for the next client-published tool result. - Any other Vercel finish reason returns `'complete'`. - If `finishReason` rejects, classify the rejection: abort-shaped errors return `'cancelled'`; anything else (for example `NoOutputGeneratedError`) returns `'error'`. The rejection guard matters: Vercel AI SDK v6 rejects `streamText().finishReason` with the abort signal's reason when the stream is aborted before any step completes, and with `NoOutputGeneratedError` when the model produced nothing at all. Without the guard the rejection would bubble out of the route handler, skip `Run.end`, and leave the run with no `ai-run-end` event on the channel. ### Parameters | Parameter | Required | Description | Type | | --- | --- | --- | --- | | pipeResult | required | The result returned by [`Run.pipe`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#pipe). |
| | finishReason | required | The `finishReason` promise from a Vercel `streamText` result. | `PromiseLike` |
| Property | Description | Type | | --- | --- | --- | | reason | Why the stream ended. | `'complete' \| 'cancelled' \| 'error'` | | error | The original error when `reason` is `'error'`. | `Error` |
### Returns `Promise`. Either a terminal reason (`'complete'`, `'cancelled'`, `'error'`) to pass to [`Run.end`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-end), or the sentinel `'suspend'` telling the caller to invoke [`Run.suspend`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-suspend) so a continuation Invocation can resume the Run. ## Example A Vercel route handler that runs a tool-capable model, pipes the stream, and either suspends (for tool resolution) or ends the run with the correct reason. ### Javascript ``` import * as Ably from 'ably'; import { streamText } from 'ai'; import { openai } from '@ai-sdk/openai'; import { Invocation } from '@ably/ai-transport'; import { createAgentSession, vercelRunOutcome } from '@ably/ai-transport/vercel'; const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); export async function POST(req: Request) { const invocation = Invocation.fromJSON(await req.json()); const session = createAgentSession({ client: ably, channelName: invocation.sessionName, }); await session.connect(); const run = session.createRun(invocation, { signal: req.signal }); try { await run.start(); await run.loadConversation(); const result = streamText({ model: openai('gpt-4'), messages: run.messages, tools: myTools, }); const pipeResult = await run.pipe(result.toUIMessageStream()); const outcome = await vercelRunOutcome(pipeResult, result.finishReason); if (outcome === 'suspend') { await run.suspend(); } else { await run.end(outcome); } } finally { session.close(); } return Response.json({ invocationId: run.invocationId }); } ``` ## Related Topics - [Chat transport](https://ably.com/docs/ai-transport/api/javascript/vercel/chat-transport.md): API reference for the AI Transport Vercel ChatTransport adapter and the Vercel-bound createClientSession and createAgentSession factories. - [Codec](https://ably.com/docs/ai-transport/api/javascript/vercel/codec.md): API reference for UIMessageCodec, the pre-built AI Transport codec for the Vercel AI SDK. ## Documentation Index To discover additional Ably documentation: 1. Fetch [llms.txt](https://ably.com/llms.txt) for the canonical list of available pages. 2. Identify relevant URLs from that index. 3. Fetch target pages as needed. Avoid using assumed or outdated documentation paths.