Vercel AI SDK
Installing Telem into a coding agent instead? The one-line curl detects the frameworks you already have and installs into as many as you select in one pass. This page is the JavaScript library integration for agents built on the Vercel AI SDK.
@telemai/sdk/vercel gives an AI SDK agent two tools, telem_search and
telem_fetch, and attaches query lineage to every call they make. You do not
build lineage data, and you do not thread a session id. The model never sees a
lineage field. When your agent starts subagents, their calls appear in the console
under the agent that started them.
Install
Section titled “Install”npm install @telemai/sdk aiai version 5 or later is necessary. It is an optional peer dependency: the main
@telemai/sdk entry does not need it, and only this subpath imports it. A
CommonJS project needs ai 5 or 6, or Node 22.12 or later with ai 7, because
ai 7 ships ES modules only.
Authentication
Section titled “Authentication”Create an API key under API keys in the Telem console.
Set it as TELEM_API_KEY, or pass it to the client in code. See
Authentication for the full rules.
Build the tool set once for one conversation, and pass it to generateText,
streamText or a ToolLoopAgent:
import { generateText } from "ai"import Telem from "@telemai/sdk"import { createTelemVercelTools } from "@telemai/sdk/vercel"
const telem = new Telem() // TELEM_API_KEY from the environmentconst tools = createTelemVercelTools({ telem, harness: "my-app" })
const result = await generateText({ model, tools, prompt: "Is the Nintendo Museum open over New Year?",})harness is a stable name for your integration. It is part of every id the
console shows, so choose it once and keep it. There is no default.
Build the set where the conversation starts, once per conversation: inside the request handler for a chat, not at the top of a module. A set shared by several conversations records all of them as one. See Conversation identity.
Subagents
Section titled “Subagents”An AI SDK subagent is a generateText that a tool starts. Give your tools to
createTelemVercelTools, and use the same tool set in the subagent. Nothing else is
necessary: the subagent’s calls appear in the console under the tool call that
started it.
import { generateText, tool } from "ai"import { z } from "zod"
const research = tool({ description: "Research one question in depth.", inputSchema: z.object({ task: z.string() }), execute: async ({ task }): Promise<string> => { const result = await generateText({ model, tools, prompt: task }) return result.text },})
const tools = createTelemVercelTools({ telem, harness: "my-app", tools: { research } })await generateText({ model, tools, prompt: "Plan a Japan trip with subagents." })Subagents started in the same turn appear side by side under the parent. A
subagent that starts its own subagents nests one level deeper. Bound that depth
in your own code: a subagent that receives the tool that started it can start
another, and a model told to split its task will split it again at every level
until the process runs out of memory. Give a subagent the tool set without the
delegating tool, or count delegations and stop at a limit. One tool call is
one subagent: a tool that starts several generateText runs shows them as one
subagent. To keep them apart, give each run its own tools with
childTelemVercelTools and a conversationId of your own. Streaming tools,
written as async function*, are linked in the same way.
This automatic link needs AsyncLocalStorage. It is a Node API that lets code
know which asynchronous call chain it is running in, the way a request id
follows a web request through every await. Node 16.4 and later, Bun, Deno,
and Cloudflare Workers with Node compatibility have it. Browsers do not, and
neither does a runtime that lacks process.getBuiltinModule, which is how the
package reaches it without a Node import: that means Node before 20.16 and edge
runtimes without Node compatibility. telemVercelAutoNesting tells you at start-up:
import { telemVercelAutoNesting } from "@telemai/sdk/vercel"if (!telemVercelAutoNesting) console.warn("subagents need childTelemVercelTools on this runtime")When it is false, the tools still work. Link each subagent with one line: see Without AsyncLocalStorage.
Your own tools
Section titled “Your own tools”To change what the model reads back from a search or a fetch, pass render. It
applies to the root and to every subagent:
const tools = createTelemVercelTools({ telem, harness: "my-app", render: { search: (response) => response.results.slice(0, 5).map((r) => ({ title: r.title, url: r.url })), fetch: (response) => response.results[0]?.content ?? "", },})To watch the calls, pass onResponse. It runs after every successful search and
fetch, for the root and for every subagent, with the agent that made the call and
the response:
const tools = createTelemVercelTools({ telem, harness: "my-app", onResponse: ({ kind, agent, response }) => { console.log(kind, agent.conversationId, response.sessionId) },})A tool of your own that calls Telem asks for the agent of its run with
telemVercelAgentFor, then calls search or fetch with the execute options the AI
SDK gave it. That is where the lineage comes from:
import { type TelemVercelToolExecutionOptions, createTelemVercelTools, telemVercelAgentFor } from "@telemai/sdk/vercel"
const lookup = tool({ description: "Look one thing up.", inputSchema: z.object({ query: z.string() }), execute: async ({ query }, options: TelemVercelToolExecutionOptions): Promise<string[]> => { const agent = await telemVercelAgentFor(tools, options) const response = await agent.search(query, { goal: "lookup" }, options) return response.results.map((r) => r.url) },})
const tools = createTelemVercelTools({ telem, harness: "my-app", tools: { lookup } })Give telemVercelAgentFor and childTelemVercelTools the set that createTelemVercelTools
returned, not a copy of it. Keep the tool keys telem_search and telem_fetch,
and do not pass tools of your own under those two names: createTelemVercelTools
refuses them, because a tool under either name would replace the one that
carries the lineage. Use render to change what they return.
Conversation identity
Section titled “Conversation identity”One agent is one conversation. The agent makes its own conversation id. If one conversation spans several requests, such as a chat where each request creates a new agent, pass the same id each time:
const tools = createTelemVercelTools({ telem, harness: "my-app", conversationId: chat.id })Without it, each request appears in the console as a separate conversation.
What the console shows
Section titled “What the console shows”- One conversation per agent, with each search and fetch in the order it happened.
- The turn that made each call, so several calls from one model turn stay together.
- A subagent’s calls under the agent that started it.
- The
goalandcontexta call carried, when the model supplied them.
Incremental transmission
Section titled “Incremental transmission”The integration sends lineage data incrementally, the same way the Telem plugins
do: a subagent’s inherited context travels once, not on every call. There is
nothing to configure. The client confirms that the backend supports it before it
omits anything, so an older or self-hosted backend keeps receiving the full data.
TELEM_INCREMENTAL=off restores full transmission on every call. It is a rollback
lever, not a tuning knob.
Without AsyncLocalStorage
Section titled “Without AsyncLocalStorage”Inside the tool that starts the subagent, ask for the subagent’s tool set with
childTelemVercelTools and pass that set to the subagent. It is bound to the tool
call that started it, so the subagent’s calls appear under it. Everything else
stays the same, and the same line also works on a runtime that has
AsyncLocalStorage, if you prefer the link to be visible in your code:
import { childTelemVercelTools, createTelemVercelTools } from "@telemai/sdk/vercel"
const research = tool({ description: "Research one question in depth.", inputSchema: z.object({ task: z.string() }), execute: async ({ task }, options): Promise<string> => { const subTools = await childTelemVercelTools(tools, options) // the one line const result = await generateText({ model, tools: subTools, prompt: task }) return result.text },})
const tools = createTelemVercelTools({ telem, harness: "my-app", tools: { research } })A subagent that starts its own subagents does the same. The line above works
unchanged one level down, because the set knows which tool call it runs in.
The runnable example, examples/vercel-ai-agents.mjs in the SDK source, shows
that line as a comment in its delegating tool.
Underneath both is createTelemVercelAgent: one agent is one conversation,
agent.tools are the same two tools, and await agent.child(options) gives a
subagent its own agent. Use it to name each conversation yourself.
Troubleshooting
Section titled “Troubleshooting”createTelemVercelAgent requires harness
Section titled “createTelemVercelAgent requires harness”Pass harness, a stable name for your integration. It is never defaulted.
A subagent shows as a separate conversation
Section titled “A subagent shows as a separate conversation”Pass the tool that starts it to createTelemVercelTools, and use the same tool set
inside the subagent. If telemVercelAutoNesting is false on your runtime, give the
subagent await childTelemVercelTools(tools, options) instead. See
Without AsyncLocalStorage.
The tools are not in the model’s tool list
Section titled “The tools are not in the model’s tool list”Keep the tool keys telem_search and telem_fetch when you spread them. The
console recognizes searches by those names.
Each request shows as its own conversation
Section titled “Each request shows as its own conversation”Pass a stable conversationId when one conversation spans several requests. See
Conversation identity.
Several conversations show as one
Section titled “Several conversations show as one”The set was built once and shared, for example at the top of a module. Build it where each conversation starts.
childTelemVercelTools: pass the set that createTelemVercelTools returned
Section titled “childTelemVercelTools: pass the set that createTelemVercelTools returned”A spread copy of the set, such as one with a replaced tool, is not the set. Keep
the original reference for childTelemVercelTools and telemVercelAgentFor, and use
render or onResponse instead of replacing a tool.
Cannot find module 'ai'
Section titled “Cannot find module 'ai'”Install ai (version 5 or later) next to @telemai/sdk. Only the vercel
subpath needs it.