# vercelRunOutcome
`vercelRunOutcome` resolves the outcome of a Vercel `streamText` response that was piped through `Run.pipe`. It returns a [`VercelRunOutcome`](#vercel-run-outcome-returns) object discriminated on `reason`: when `reason` is `'suspend'`, call `Run.suspend`; otherwise pass the whole outcome to `Run.end` (the non-`'suspend'` arms are assignable to `RunEndParams`).
It preserves transport-level outcomes (`'cancelled'`, `'error'`) from the pipe result. When the pipe completed naturally, it awaits Vercel's `finishReason` and returns `{ reason: '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 `{ reason: 'complete' }` otherwise. An `'error'` outcome carries the wrapped `error`.
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.reason === '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. An `'error'` outcome carries `pipeResult.error` wrapped as an `Ably.ErrorInfo`.
2. If `pipeResult.reason` is `'complete'`, await `finishReason` and translate:
- `'tool-calls'` returns `{ reason: '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 `{ reason: 'complete' }`.
- If `finishReason` rejects, classify the rejection: abort-shaped errors return `{ reason: 'cancelled' }`; anything else (for example `NoOutputGeneratedError`) returns `{ reason: 'error', error }` with the rejection wrapped as an `Ably.ErrorInfo`.
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 session.
### 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`. A `VercelRunOutcome` is an object discriminated on `reason` with three arms:
- `{ reason: 'suspend' }`: the LLM requested tools the SDK did not auto-execute. Call [`Run.suspend`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-suspend) so a continuation invocation can resume the run.
- `{ reason: 'complete' | 'cancelled' }`: the run terminated normally. Pass the outcome to [`Run.end`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-end).
- `{ reason: 'error', error }`: the run failed. `error` is an `Ably.ErrorInfo`. Pass the outcome to [`Run.end`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-end).
`error` is present only on the `'error'` arm. The non-`'suspend'` arms are assignable to [`RunEndParams`](https://ably.com/docs/ai-transport/api/javascript/core/agent-session.md#run-end-params), so after a `'suspend'` guard the whole object passes straight to `Run.end(outcome)`.
| Property | Description | Type |
| --- | --- | --- |
| reason | The terminal reason, or `'suspend'` to pause the run. | `'suspend' \| 'complete' \| 'cancelled' \| 'error'` |
| error | The terminal error, present only when `reason` is `'error'`. | `Ably.ErrorInfo` |
## 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 {
// Rebuild the conversation from run.view before run.start(): draining pages
// in this run's triggering input (otherwise run.start() awaits it live).
while (run.view.hasOlder()) {
await run.view.loadOlder();
}
const conversation = run.view.getMessages().map(({ message }) => message);
await run.start();
const result = streamText({
model: openai('gpt-4'),
messages: conversation,
tools: myTools,
});
const pipeResult = await run.pipe(result.toUIMessageStream());
const outcome = await vercelRunOutcome(pipeResult, result.finishReason);
if (outcome.reason === 'suspend') {
await run.suspend();
} else {
await run.end(outcome);
}
} finally {
await session.end();
}
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 createUIMessageCodec, the factory for 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.