npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cogitator-ai/ai-sdk

v0.10.1

Published

Vercel AI SDK adapter for Cogitator - bidirectional compatibility

Readme

@cogitator-ai/ai-sdk

Vercel AI SDK adapter for Cogitator — bidirectional compatibility between Cogitator agents and the AI SDK, for AI SDK 4, 5, 6 and 7.

Full guide: cogitator.app/docs/integrations/ai-sdk

Installation

pnpm add @cogitator-ai/ai-sdk @cogitator-ai/core ai

ai is a peer dependency: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0.

Features

  • Use Cogitator agents with the AI SDK — generateText, streamText, structured output, any AI SDK major
  • Use AI SDK models in Cogitator — wrap any AI SDK provider model (LanguageModelV1 – LanguageModelV4) as a Cogitator backend
  • Tool conversion — convert tools in both directions; converted tools work with the tools option of every AI SDK major

Compatibility

| AI SDK | Model specs it accepts | Default cogitatorModel() spec | fromAISDK() input | | ------ | ---------------------- | ------------------------------- | ------------------- | | ai@4 | v1 | v1 | LanguageModelV1 | | ai@5 | v2 | v2 | LanguageModelV2 | | ai@6 | v2, v3 | v3 | LanguageModelV3 | | ai@7 | v2, v3, v4 | v4 | LanguageModelV4 |

Specification auto-detection

When specificationVersion is omitted, cogitatorModel() and createCogitatorProvider() pick the spec of the installed ai package, so existing code keeps working on every major:

  • Runtime: ai/package.json is resolved from process.cwd() (your app), then from this package — also for ESM-only installs whose exports hide package.json. The result is cached per process. If ai cannot be resolved (for example in runtimes without process.getBuiltinModule), 'v2' is used, which ai@5 – ai@7 accept.
  • Types: the default return type is derived from the LanguageModel type of the ai package your TypeScript resolves, with the same mapping, so generateText({ model: cogitatorModel(cog, agent) }) type-checks on ai@4 – ai@7 without changes.

Pass specificationVersion to override the detection — for example when several ai majors are installed, or when the runtime and the type-level ai differ. AI SDK 6 and 7 run v2 models in a compatibility mode that logs a "Using v2 specification compatibility mode" warning per call; the detected v3 / v4 defaults avoid it.

Usage

Cogitator Agent as AI SDK Model

import { generateText, streamText } from 'ai';
import { Cogitator, Agent, tool } from '@cogitator-ai/core';
import { cogitatorModel } from '@cogitator-ai/ai-sdk';
import { z } from 'zod';

const cog = new Cogitator({
  llm: { providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } } },
});

const searchTool = tool({
  name: 'search',
  description: 'Search the web',
  parameters: z.object({ query: z.string() }),
  execute: async ({ query }) => ({ results: [`Result for: ${query}`] }),
});

const researcher = new Agent({
  name: 'researcher',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You are a research assistant.',
  tools: [searchTool],
});

// spec detected from the installed ai package (ai@4 – ai@7)
const { text } = await generateText({
  model: cogitatorModel(cog, researcher),
  prompt: 'Research the latest AI developments',
});

// explicit override
const result = streamText({
  model: cogitatorModel(cog, researcher, { specificationVersion: 'v2' }),
  prompt: 'Write an article about TypeScript',
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

With ai@4 the same call returns a LanguageModelV1; { specificationVersion: 'v1' } forces it explicitly.

How the agent maps to a model

  • The agent runs its whole loop (LLM calls and its own tool calls) inside one model call. The result's finishReason says how the run ended: stop for an answer, length when the answer stopped at the token limit (RunResult.truncated), content-filter when it was withheld (raw content_filter for a provider's filter, refusal when the model declined), other (raw iteration-limit) when tool calls used up the agent's maxIterations, tool-calls when the run waits for tool approvals, and other (raw paused) when it waits only on questions of its tools (below). v1 and v2 models get the same reasons in their form (other for a paused run). providerMetadata.cogitator also carries truncated, blocked and iterationLimitReached.
  • generateText and streamText see the same text: what the agent writes before a tool call ("Let me check.") comes before that call in both, as text parts of the response. In JSON mode (Output.object, generateObject) only the agent's final answer is text, so a preamble never breaks the parsed object, and a stream then delivers the JSON when the run ends.
  • The agent's tool calls are reported as provider-executed tool calls with their results (result.toolCalls / result.toolResults, tool-call / tool-result stream parts). The AI SDK never executes them again.
    • With v3 / v4 models they are always reported.
    • With v2 models they are reported only for tools you also pass in tools (ai@5 cannot handle provider-executed calls of undeclared tools).
    • Every model also lists them in providerMetadata.cogitator.toolCalls, together with runId, threadId, agentId, model, cost and duration.
  • The trailing user messages of the prompt are the agent's input, and the turns before them reach the run as its history (RunOptions.history), as messages: user and assistant text, assistant tool calls and their results, so the model sees earlier turns the way it wrote them. System messages, files and images are not forwarded (the agent's instructions are its system prompt) and the AI SDK reports a warning. A prompt that does not end with a user message is refused, except one that answers tool approvals.
  • providerOptions.cogitator (providerMetadata.cogitator on ai@4) takes threadId, userId and saveHistory. With a threadId the run's history is that memory thread, so only the new turn of the prompt is used, and the turn is saved there (saveHistory defaults to true). Without one the prompt is the whole conversation and nothing is saved unless saveHistory: true. userId reaches memory, tools (context.userId) and resumes.
  • v4 models report what happens inside the run as custom parts: cogitator.handoff when the agent hands the conversation to another (from, to, reason) and cogitator.model-fallback when a turn moves to a fallback model (from, to, error), with the details in providerMetadata.cogitator, in the answer's content and in the stream as they happen.
  • temperature, topP, maxOutputTokens (maxTokens on ai@4) and stopSequences override the agent settings for the call; abortSignal cancels the run, as does cancelling the stream.
  • The reasoning call option of v4 models (ai@7) overrides the effort of the agent's reasoning for the call. An agent with reasoning: { summary: true } returns its summary as reasoning content (reasoning-start / reasoning-delta / reasoning-end stream parts, reasoning text on ai@4), and usage carries its reasoning and cached input tokens.
  • JSON response formats (generateObject, Output.object, ai@4 object-json / object-tool modes) switch the agent to JSON mode and append the schema to the input.
  • AI SDK tools the agent does not own cannot be called by the agent; the model reports a warning for each of them.
  • toolChoice: 'none' runs the agent without its tools and handoffs (tools registered on the Cogitator itself stay, with a warning). required and a named tool of the agent become the run's toolChoice: the model must call a tool (or that one) before it answers, and the run goes back to auto once it has. A named tool the agent does not have comes back as an unsupported-setting warning.
  • When the agent calls a tool that needs approval (requiresApproval), its run pauses and the call never passes for an answer. v3 and v4 models (ai@6, ai@7) end the turn with finishReason: 'tool-calls' and a tool-approval-request for each waiting call (a provider-executed tool-call comes with it). Answer with a tool-approval-response (providerExecuted: true, which convertToModelMessages sets for UI messages) as the last message of the next prompt and the run resumes: approved calls run, denied ones are declined with your reason. v1 and v2 models cannot ask, so they finish with finishReason: 'other' and a warning, and providerMetadata.cogitator holds status: 'paused', the threadId and pendingApprovals to resume with cogitator.resume(agent, threadId, { decisions }).
  • A question a tool asks with context.interrupt() pauses the run too, and no AI SDK version has a part that asks it. Every model version then finishes with finishReason: 'other' (raw paused on v3 and v4) and a warning that names each question, and providerMetadata.cogitator holds status: 'paused', the threadId and pendingQuestions (id, reason, message, responseSchema) to answer with cogitator.resume(agent, threadId, { responses: { [id]: answer } }).
const paused = await generateText({ model: cogitatorModel(cog, agent), prompt: 'Refund A-1' });
const request = paused.content.find((part) => part.type === 'tool-approval-request');

if (request?.type === 'tool-approval-request') {
  const done = await generateText({
    model: cogitatorModel(cog, agent),
    messages: [
      { role: 'user', content: 'Refund A-1' },
      ...paused.response.messages,
      {
        role: 'tool',
        content: [
          {
            type: 'tool-approval-response',
            approvalId: request.approvalId,
            approved: true,
            providerExecuted: true,
          },
        ],
      },
    ],
  });
}

Named Agents

import { createCogitatorProvider } from '@cogitator-ai/ai-sdk';

const provider = createCogitatorProvider(cog, {
  agents: [researcher, writer],
  specificationVersion: 'v2', // optional, detected from the installed ai package
});

const model = provider('researcher', { temperature: 0.5 });
const sameModel = provider.languageModel('researcher');

The provider follows the specification of its models, so an AI SDK provider registry takes it. Agents are its language models, and its embedding and image models (and an unknown agent) throw NoSuchModelError:

import { createProviderRegistry, generateText } from 'ai';

const registry = createProviderRegistry({ cogitator: provider, openai });
const { text } = await generateText({ model: registry.languageModel('cogitator:writer'), prompt });

Cogitator Agent as AI SDK Agent

toAISDKAgent (ai@7) turns a Cogitator agent into an AI SDK agent (agent-v1) for everything that takes one, such as createAgentUIStreamResponse. It is a ToolLoopAgent on cogitatorModel(): the Cogitator agent runs its own tools, memory and approvals in one model step.

import { toAISDKAgent } from '@cogitator-ai/ai-sdk/agent';

const support = toAISDKAgent(cog, supportAgent, { temperature: 0.2 });
const { text } = await support.generate({ prompt: 'Where is order 42?' });

It takes the settings of ToolLoopAgent except model, instructions and the tool settings, which the Cogitator agent has itself, and is in its own entry point because older ai versions have no Agent type.

AI SDK Models in Cogitator

fromAISDK() wraps any AI SDK language model — LanguageModelV1 (ai@4 providers) through LanguageModelV4 (ai@7 providers) — as a Cogitator LLMBackend:

import { google } from '@ai-sdk/google';
import { fromAISDK } from '@cogitator-ai/ai-sdk';

const backend = fromAISDK(google('gemini-3.5-flash-lite'));

const messages = [
  { role: 'system' as const, content: 'Be brief.' },
  { role: 'user' as const, content: 'Write a haiku about programming' },
];

const response = await backend.chat({ model: 'gemini-3.5-flash-lite', messages });

for await (const chunk of backend.chatStream({ model: 'gemini-3.5-flash-lite', messages })) {
  process.stdout.write(chunk.delta.content ?? '');
}

The backend converts Cogitator messages (text, images, assistant tool calls and tool results), tools, toolChoice, responseFormat, sampling settings and the abort signal to the model's specification, and maps text, reasoning, tool calls, finish reasons and usage (including cached, cache-write and reasoning tokens) back: reasoning content and stream parts become ChatResponse.reasoning / delta.reasoning. ChatRequest.reasoning.effort is sent as the reasoning call option of v4 models (max as xhigh). Older specifications have no provider-neutral reasoning setting, so configure effort, budgets and summaries on the AI SDK provider model (its providerOptions or settings). Gemini thought signatures on tool calls are preserved across turns, and so are the reasoning parts before a tool call with their provider metadata (Anthropic thinking signatures, redacted thinking) and each tool call's own provider metadata, which models with extended thinking need on the turn after a tool call. Tool results with images go to the model as images (content output with media, image-data or file parts by specification, content on ai@4), and failed tool calls as error-text.

To run Cogitator agents on an AI SDK model, register the backend under a name in llm.backends and point agents at it with name/model:

import { Agent, Cogitator } from '@cogitator-ai/core';

const cog = new Cogitator({
  llm: { backends: { gemini: fromAISDK(google('gemini-3.5-flash-lite')) } },
});

const agent = new Agent({
  name: 'writer',
  model: 'gemini/gemini-3.5-flash-lite',
  instructions: 'You write short poems.',
});

const result = await cog.run(agent, { input: 'A haiku about types' });

Tools, memory, streaming and every other run feature work as with built-in providers. The wrapped model is used whatever model name the request carries; cost is reported only for models the price registry knows.

Tool Conversion

import { tool as aiTool } from 'ai';
import { tool as cogTool } from '@cogitator-ai/core';
import { fromAISDKTool, toAISDKTool, convertToolsToAISDK } from '@cogitator-ai/ai-sdk';
import { z } from 'zod';

// AI SDK tool → Cogitator tool (ai@5+ `inputSchema` or ai@4 `parameters`)
const aiWeather = aiTool({
  description: 'Get weather for a city',
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }) => ({ temp: 20, city }),
});
const cogWeather = fromAISDKTool(aiWeather, 'weather');

// Cogitator tool → AI SDK tool (works with the `tools` option of ai@4 – ai@7)
const cogCalculator = cogTool({
  name: 'calculator',
  description: 'Perform calculations',
  parameters: z.object({ expression: z.string() }),
  execute: async ({ expression }) => ({ result: evaluate(expression) }),
});
const aiCalculator = toAISDKTool(cogCalculator);
const aiTools = convertToolsToAISDK([cogCalculator]);

toAISDKTool() returns a tool with both schema fields:

  • inputSchema (read by ai@5 – ai@7) — the Cogitator tool's zod schema
  • parameters (read by ai@4) — an AI SDK Schema with draft-07 JSON Schema and synchronous validation; it is also a Standard JSON Schema

execute(input, options) maps the AI SDK options to a Cogitator ToolContext: toolCallId becomes toolCallId (and runId, unless the tool context names one), abortSignal becomes signal, and string fields agentId, runId, threadId, userId, channelType, channelId, topicId, conversationId of the tool context (context on ai@7, experimental_context on ai@5 / ai@6) are copied over.

A tool result with media (toolContent() of @cogitator-ai/core, or an object with a base64 image such as a browser screenshot) reaches the model of generateText / streamText as an image through the tool's toModelOutput (experimental_toToolResultContent on ai@4). Files are described, other results go as text or JSON as usual. A toolOutput() result reaches the model as its content only, while the application gets the whole result back from the AI SDK, details and presentation included (with an invoker, the call goes through invokeTool and the application gets content). In the other direction, fromAISDKTool() gives the AI SDK tool the id of the call it runs for as toolCallId, and keeps the $defs of its JSON Schema.

Approval flags survive both ways: toAISDKTool() sets needsApproval from requiresApproval, so ai@6 and ai@7 ask before running the tool, and fromAISDKTool() sets requiresApproval from needsApproval, so a Cogitator run pauses (a needsApproval function that answers with a promise counts as "needs approval").

Running converted tools through Cogitator

Without options the AI SDK calls the tool's execute directly, so the tool runs without the guardrails, the sandbox of a sandboxed tool or tool.timeout, and ai@4 and ai@5, which do not read needsApproval, run a tool that needs approval without asking. Pass your Cogitator as invoker and every call goes through cogitator.invokeTool, the way an agent run executes it:

import { generateText } from 'ai';
import { convertToolsToAISDK } from '@cogitator-ai/ai-sdk';

const { text } = await generateText({
  model,
  tools: convertToolsToAISDK([refund, lookupOrder], {
    invoker: cogitator,
    onApproval: async (request) => ({ approved: await askUser(request) }),
  }),
  prompt: 'Refund order A-1',
});
  • A call the AI SDK asks approval for (the tool's requiresApproval holds for its input) and that the AI SDK approval flow approved (ai@6, ai@7: an approving tool-approval-response for the call in the last message, as the AI SDK reads it) runs without being asked again. In most apps those messages come from the client, so set the AI SDK's experimental_toolApprovalSecret to have it refuse approvals your server did not sign. An approval in the messages for a call the AI SDK never asks about counts for nothing.
  • Any other call that needs approval, every one on ai@4 and ai@5, or one the guardrails want approved, goes to onApproval, then to guardrails.onToolApproval. A call nobody decides fails with Tool "refund" needs approval, and nobody approved this call, a declined one with its reason, and the model reads the error.
  • ai@7 can also decide approval for a whole call with toolApproval, which leaves no approval response in the messages: with an invoker, decide those calls in onApproval.
  • The AI SDK already parsed the arguments with the tool's schema, so they are not parsed again and transforms apply once.

fromAISDKTool() accepts zod 4 schemas, Standard JSON Schemas, jsonSchema() / zodSchema() schemas from any AI SDK major and plain JSON Schema objects. Non-zod schemas are converted to JSON Schema and validated before the AI SDK execute runs. It also resolves ai@7 dynamic descriptions and the final value of streaming (async function*) execute functions, and passes the Cogitator context to the AI SDK tool as context / experimental_context. Zod 3 schemas cannot be converted to JSON Schema here — wrap them with zodSchema() from ai. The tool name (the second argument, else the tool's own name) must be one every LLM provider accepts, as for tool() of @cogitator-ai/core: fromAISDKTool() throws a TypeError otherwise, and normalizeToolName() of core turns an object key such as weather.get into weather_get.

API Reference

cogitatorModel(cogitator, agent, options?)

function cogitatorModel<V extends CogitatorSpecificationVersion = DefaultSpecificationVersion>(
  cogitator: Cogitator,
  agent: Agent,
  options?: CogitatorModelOptions<V>
): CogitatorLanguageModel<V>;

type CogitatorSpecificationVersion = 'v1' | 'v2' | 'v3' | 'v4';

// 'v1' | 'v2' | 'v3' | 'v4', derived from the installed ai package's LanguageModel type
type DefaultSpecificationVersion;

interface CogitatorModelOptions<V> {
  temperature?: number;
  maxTokens?: number;
  topP?: number;
  specificationVersion?: V;
}

CogitatorLanguageModel<'v1'> is a LanguageModelV1; 'v2', 'v3', 'v4' return CogitatorLanguageModelV2 / V3 / V4, which satisfy LanguageModelV2 / V3 / V4 of every @ai-sdk/provider release.

createCogitatorProvider(cogitator, config)

function createCogitatorProvider<
  V extends CogitatorSpecificationVersion = DefaultSpecificationVersion,
>(cogitator: Cogitator, config: CogitatorProviderConfig<V>): CogitatorProvider<V>;

interface CogitatorProviderConfig<V> {
  agents: Agent[] | Map<string, Agent> | Record<string, Agent>;
  specificationVersion?: V;
}

The provider has the members of its specification (CogitatorProviderMembers<V>): specificationVersion, embeddingModel and imageModel for v4 and v3, textEmbeddingModel and imageModel for v2, textEmbeddingModel for v1, all throwing NoSuchModelError.

toAISDKAgent(cogitator, agent, settings?) (@cogitator-ai/ai-sdk/agent)

A ToolLoopAgent of ai@7 on cogitatorModel(cogitator, agent, { specificationVersion: 'v4' }), with the id of the agent's name. settings (CogitatorAgentSettings) are those of ToolLoopAgent without model, instructions, tools, activeTools and toolChoice.

providerOptions.cogitator (CogitatorCallOptions)

| Field | Type | Description | | ------------- | --------- | ------------------------------------------------------------------------------------------- | | threadId | string | Memory thread of the run: its history comes from memory, the prompt gives only the new turn | | userId | string | The user the run belongs to | | saveHistory | boolean | Save the turn to memory. Default: true with a threadId, false without |

fromAISDK(model) / new AISDKBackend(model)

function fromAISDK(model: AISDKLanguageModel): LLMBackend;

type AISDKLanguageModel = LanguageModelV1 | LanguageModelV2 | LanguageModelV3 | LanguageModelV4;

Tools

function fromAISDKTool<TParams, TResult>(
  aiTool: AISDKToolLike,
  toolName?: string
): Tool<TParams, TResult>;
function toAISDKTool<TParams, TResult>(
  cogTool: Tool<TParams, TResult>,
  options?: AISDKToolOptions
): AISDKTool<TParams, TResult>;
function convertToolsFromAISDK(aiTools: Record<string, AISDKToolLike>): Tool[];
function convertToolsToAISDK(
  cogTools: Tool[],
  options?: AISDKToolOptions
): Record<string, AISDKTool>;

interface AISDKToolOptions {
  invoker?: ToolInvoker; // a Cogitator: calls go through invokeTool
  onApproval?: RunOptions['onApproval']; // decides calls the AI SDK did not approve
}

Migrating from 0.2.x

  • cogitatorModel() and createCogitatorProvider() detect the installed ai major and return the matching model spec (v1 on ai@4, as before); specificationVersion overrides it.
  • Agent tool calls are no longer reported as client tool calls with finishReason: 'tool-calls' (which made the AI SDK execute them again or fail with NoSuchToolError); see How the agent maps to a model.
  • toAISDKTool() now emits inputSchema (ai@5+) next to parameters (ai@4).
  • fromAISDK() accepts LanguageModelV1 – LanguageModelV4 and forwards tool calls and tool results as structured prompt parts instead of flattened text.

Documentation

License

MIT