SDKs and frameworks
Vercel AI SDK
Give your Vercel AI SDK agent the Telem search and fetch tools, with query lineage on every call.
Using a coding agent? Find the one you use on Integrations.
@telemai/sdk/vercel gives an AI SDK agent two tools, telem_search and
telem_fetch. It 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. This is
because ai 7 ships ES modules only.
Authentication
Section titled “Authentication”Create an API key under API keys in the Telem console.
The guided installer writes it to ~/.telem/credentials.json, which the client
reads on Node 20.16 or later. You can also set it as TELEM_API_KEY, or pass it in code. See
API keys 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() // environment, then ~/.telem/credentials.jsonconst 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. Limit that depth
in your own code. A subagent that receives the tool that started it can start
another. 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. It lets code
know which asynchronous call chain it runs 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 have it. A
runtime that lacks process.getBuiltinModule cannot use it either, because the
package reaches it through that function, without a Node import. This 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.
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.
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. It gets 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 first gets the agent of its run with
telemVercelAgentFor. Then it calls search or fetch with the execute options that
the AI SDK gave it. This is how the call gets its lineage:
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.
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
sends the lineage. Use render to change what they return.
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
goalandcontextthat a call sent, 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 is sent once, not on every call. It needs no
configuration. Before the client omits anything, it confirms that the backend
supports this. So an older or self-hosted backend still gets the full data.
TELEM_INCREMENTAL=off restores full transmission on every call. Use it only to
roll back this behavior.
Without AsyncLocalStorage
Section titled “Without AsyncLocalStorage”Inside the tool that starts the subagent, get the subagent’s tool set with
childTelemVercelTools. Pass that set to the subagent. The set is bound to the
tool call that started it, so the subagent’s calls appear under it. Everything
else stays the same. The same line also works on a runtime that has
AsyncLocalStorage, if you want 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 is examples/vercel-ai-agents.mjs in the SDK source. It
shows that line as a comment in its delegating tool.
Both ways use createTelemVercelAgent underneath. One agent is one conversation,
and agent.tools are the same two tools. await agent.child(options) gives a
subagent its own agent. Use createTelemVercelAgent 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 has no default.
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.
Next steps
Section titled “Next steps”- JavaScript SDK: the client, search options and lineage helpers
- Search parameters: choose providers, tiers and date windows
- Fetch parameters: options for reading pages