# 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.