Skip to Content
TensorPass
Reference

@aiconnect/core

Every public export of @aiconnect/core, the site's connection to the visitor's AI, the fetch for AI SDKs, the tool loop and the MCP tool source, with its types generated from the source and an example.

@aiconnect/core is the site's side of AI Connect, without any framework. It exports the AiConnect client, the McpToolSource class, the runConversation and finalAnswer functions, the chat content helpers, WalletRequestError, and the types they share with AI Wallet.

The tables on this page are generated from the package's TypeScript source each time the docs are built. Open a row to see its full type.

import {
  AiConnect, McpToolSource, WalletRequestError, contentParts, dataUrl, finalAnswer, inlineData, runConversation, textOf,
} from '@aiconnect/core';
import type {
  ChatEvent, ChatMessage, ChatResponse, ChatStart, ConnectionState, ContentPart, GenerationHint, JsonSchema, ToolSource,
  Usage, UserMessage, WalletChatParams, WalletLink, WalletProvider,
} from '@aiconnect/core';

AiConnect

The site's connection to the visitor's AI, through AI Wallet. Its public members:

Prop

Type

  • constructor. new AiConnect() takes no options. In a browser, the client looks for the wallet at once and asks for the site's status. A site the visitor already approved becomes connected without a prompt. Built where window does not exist, during a server render, the client does nothing and stays checking.
  • installUrl. The AI Wallet page, https://tensorpass.app/wallet, where a visitor without the wallet gets it. The connect window of @aiconnect/react links to it in the noWallet state.
  • state. The current connection state.
  • subscribe(listener). Calls listener with each new state and returns a function that removes it. The listener is not called with the current state when you subscribe, so read state first.
  • connect(). Asks the wallet to let this site use the visitor's AI. Without a wallet the state becomes noWallet. Otherwise it moves to connecting, then to the state the wallet's answer gives, usually connected, or to error with the wallet's reason, such as "The connection was declined". The promise resolves in each of these cases. A locked wallet first asks the visitor to unlock it, and the wallet asks the visitor to approve the site the first time it connects.
  • disconnect(). Asks the wallet to remove this site from the visitor's approved sites, then sets the state to disconnected, or to noWallet without a wallet. The next connect() asks the visitor again. The promise rejects when the wallet refuses, for example "The wallet is locked".
  • fetch. A fetch for the AI SDKs, which answers their requests through the wallet in their own protocol. See WalletFetch.
  • sdkOptions. The options an OpenAI or Anthropic SDK client takes to answer through the wallet. See SdkOptions.

The state also changes on its own when the visitor acts in the wallet: picking another account for the site, removing the site, or unlocking the wallet.

const client = new AiConnect();

client.subscribe((state) => {
  if (state.status === 'connected') {
    console.log(`Answered by ${state.providerName}`);
  }
});

document.querySelector('#connect')?.addEventListener('click', () => client.connect());

ConnectionState

A union of six states, one per status. Each row holds the shape of the state with that status.

Prop

Type

StatusMeaning
checkingThe client is looking for the wallet, or the page is rendering on a server.
noWalletNo wallet answered within 300 milliseconds of the page asking for one.
disconnectedThe wallet answered, and this site is not connected. A wallet that is locked when the page loads reports the site this way until the visitor unlocks it.
connectingconnect() is waiting for the visitor in the wallet.
connectedThe site may chat. providerName is the display name of the account that answers it, or Auto. model is that account's default model, absent with Auto. session sends chats to the wallet.
errorThe last connect() failed. message holds the reason.
function label(state: ConnectionState): string {
  switch (state.status) {
    case 'connected':
      return state.model ? `${state.providerName} (${state.model})` : state.providerName;
    case 'error':
      return state.message;
    default:
      return state.status;
  }
}

SdkOptions

The type of client.sdkOptions: the options an OpenAI or Anthropic SDK client takes to answer through the wallet.

Prop

Type

  • apiKey. A placeholder, since the SDKs require a key. The wallet does not read it.
  • fetch. The client's fetch.
  • dangerouslyAllowBrowser. true. The SDKs guard real keys from the browser, and no real key reaches the page.
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';

const openai = new OpenAI(client.sdkOptions);
const anthropic = new Anthropic(client.sdkOptions);

WalletFetch

The type of client.fetch, a function with the signature of the global fetch:

Prop

Type

It lets the AI SDK a site already uses answer through the visitor's AI. The request stays in the page: fetch reads neither the host the SDK targets nor its API key.

  • It answers POST requests whose path ends with /chat/completions (OpenAI Chat Completions), /responses (OpenAI Responses), /messages (Anthropic Messages), or :generateContent and :streamGenerateContent (Gemini). Any other request gets a 404 with a JSON error in the shape the OpenAI, Anthropic and Google SDKs read.
  • It reads the request into the wallet's chat format, asks the wallet, and answers in the same protocol, streamed or whole. The wallet answers with the account the visitor picked for the site, and the response names the model that answered. The model the request names is not used.
  • It waits for the status check or the visitor's approval to end. A site that is not connected gets a 401 in the protocol's error shape, with "AI Wallet is not connected on this site" or "AI Wallet is not installed in this browser". Called outside a browser, it answers 401 with "AI Wallet answers in the browser only".
  • An answer that fails before any text comes back as an HTTP error with the wallet's status, so each SDK raises its own typed error. Aborting the SDK's signal stops the chat in the wallet.

The SDKs that take a fetch of their own receive it directly:

import { createOpenAI } from '@ai-sdk/openai';
import { GoogleGenAI } from '@google/genai';

const openai = createOpenAI({ apiKey: client.sdkOptions.apiKey, fetch: client.fetch });
const google = new GoogleGenAI({ apiKey: client.sdkOptions.apiKey, httpOptions: { fetch: client.fetch } });

The existing SDKs guide covers each SDK in full.

WalletRequestError

A request the wallet refused or could not answer. It extends Error with the request's HTTP status.

Prop

Type

StatusCause
The provider's statusThe provider refused the call.
401The visitor has not connected the site.
403The visitor declined the connection, the wallet is locked, or it holds no account.
499The visitor stopped the answer in AI Wallet.

AI Wallet builds its errors with its own copy of the class, so a site reads name and status and leaves instanceof aside. A chat aborted by the site's own signal rejects with the signal's reason instead.

try {
  transcript.push(...await runConversation({ session: state.session, tools, messages: transcript }));
} catch (error) {
  const { name, status } = error as { name?: string; status?: number };
  if (name === 'WalletRequestError' && status === 499) {
    showNotice('You stopped the answer in AI Wallet.');
  }
}

McpToolSource

The site's tools, read from its MCP server over Streamable HTTP. It implements ToolSource.

Prop

Type

  • constructor. new McpToolSource(url, clientName) takes the server's address, as a string or a URL. A relative URL resolves against the page's address, so outside a page pass an absolute one. clientName is the client name your MCP server sees when the client initializes, aiconnect by default.
  • listTools(). Connects on first use, reads the tool list once and keeps it for the life of the instance. A failed attempt is forgotten, so the next call asks the server again.
  • callTool(name, args). Returns the result's text parts joined with line breaks. A result marked isError comes back as Tool error: followed by its text.
const tools = new McpToolSource('/api/mcp', 'Bookshop');

const definitions = await tools.listTools();
const result = await tools.callTool('search_books', { mood: 'calm' });

The MCP guide covers the server side.

runConversation

Runs the agent loop in the browser: the model answers or asks for tools, the site's tools run, their results go back to the model, until it answers in plain text.

Prop

Type

It returns the messages the conversation gained: the assistant turns and the tool results, ending with the final assistant answer. It leaves options.messages untouched, so append the result to your transcript yourself.

  • It lists the tools once, then sends messages and the tool definitions to session.chat. It sends no generation options; call session.chat directly to set them.
  • It runs the tools the model asks for one after the other, in the model's order, and adds each result as a tool message.
  • A tool that throws becomes a result with the text Tool error: and the error's message, which the model reads.
  • It stops when an answer has no tool calls, when no tools were given, or after maxSteps steps (6 by default). The last step offers no tools, so the model has to conclude.
  • An error from session.chat, such as a provider refusal or the visitor stopping the answer in the wallet, rejects the promise.
const { state } = client;
if (state.status === 'connected') {
  const asked: ChatMessage = { role: 'user', content: 'A calm book for tonight' };
  const added = await runConversation({
    session: state.session,
    tools,
    messages: [{ role: 'system', content: SYSTEM }, asked],
    onText: (delta) => output.append(delta),
  });
  transcript.push(asked, ...added);
}

ConversationOptions

Prop

Type

  • session. The session of a connected state.
  • messages. The conversation so far, system prompt first.
  • tools. The tool source. Without it, the model answers once.
  • maxSteps. The number of model calls at most. The default is 6.
  • signal. Passed to session.chat on each step. With the wallet's session, aborting it stops the chat in AI Wallet, and the promise rejects with the signal's reason.
  • onText, onToolCall, onToolResult. The hooks.
const controller = new AbortController();
stopButton.onclick = () => controller.abort();

const added = await runConversation({ session: state.session, tools, messages, maxSteps: 3, signal: controller.signal });

ConversationHooks

Prop

Type

  • onText. Each piece of the answer's text as it streams, across every step of the loop.
  • onToolCall. A tool call, before the tool runs.
  • onToolResult. The same call with its result, after the tool ran or failed.
await runConversation({
  session: state.session,
  tools,
  messages,
  onToolCall: (call) => showStatus(`Running ${call.name}`),
  onToolResult: (call, result) => renderCards(call.name, result),
});

finalAnswer

Prop

Type

Returns the last message when it is an assistant message, and null otherwise.

const added = await runConversation({ session: state.session, tools, messages });
const answer = finalAnswer(added)?.content ?? '';

Chat content helpers

The functions that read a message's content, as parts or as text, and an image's data: URL as its media type and base64 data. AI Wallet's connectors use them in every translation.

Prop

Type

  • contentParts(content). The content as parts. A string becomes one text part, and an empty string none.
  • textOf(content). The text of the parts joined, without the images.
  • inlineData(url). The media type and data of a base64 data: URL, or null for any other URL.
  • dataUrl(data). The data: URL of an InlineData.

InlineData

Prop

Type

const question: UserMessage = {
  role: 'user',
  content: [
    { type: 'text', text: 'What does this label say?' },
    { type: 'image', url: dataUrl({ mediaType: 'image/png', data: PNG_BASE64 }) },
  ],
};

textOf(question.content); // 'What does this label say?'

// An Anthropic image block: inline data when the URL carries it, the URL otherwise.
const blocks = contentParts(question.content).map((part) => {
  if (part.type === 'text') {
    return { type: 'text', text: part.text };
  }
  const inline = inlineData(part.url);
  return inline
    ? { type: 'image', source: { type: 'base64', media_type: inline.mediaType, data: inline.data } }
    : { type: 'image', source: { type: 'url', url: part.url } };
});

Chat types

The chat format the site and the wallet share. The wallet translates it to each provider's own format and back. ChatMessage is a union of four messages, one per role:

Prop

Type

SystemMessage

Prop

Type

UserMessage

Prop

Type

content is a string, or a list of parts when the message carries images.

AssistantMessage

Prop

Type

ToolMessage

Prop

Type

A ToolMessage answers the ToolCall whose id it carries in toolCallId.

ToolCall

Prop

Type

ContentPart

A union of two parts, one per type:

Prop

Type

TextPart

Prop

Type

ImagePart

Prop

Type

url is a data: URL, or an https URL for the providers that fetch images themselves.

const messages: ChatMessage[] = [
  { role: 'system', content: 'You recommend books from this site\'s catalog.' },
  { role: 'user', content: 'A calm book for tonight' },
  { role: 'assistant', content: '', toolCalls: [{ id: 'call_1', name: 'search_books', arguments: { mood: 'calm' } }] },
  { role: 'tool', toolCallId: 'call_1', name: 'search_books', content: '[{"title":"Piranesi"}]' },
];

const cover: ContentPart[] = [
  { type: 'text', text: 'Which book has this cover?' },
  { type: 'image', url: 'https://example.com/covers/piranesi.jpg' },
];
messages.push({ role: 'user', content: cover });

Tool types

The tools a site offers. Any object that implements ToolSource can feed runConversation and useAiChat.

ToolDefinition

Prop

Type

ToolSource

Prop

Type

JsonSchema

The type of inputSchema, a JSON Schema object:

Prop

Type

const empty: JsonSchema = { type: 'object', properties: {} };

const clock: ToolSource = {
  listTools: async () => [{
    name: 'current_time',
    description: 'The current time of the visitor\'s computer, in UTC, as an ISO 8601 string.',
    inputSchema: empty,
  }],
  callTool: async () => new Date().toISOString(),
};

GenerationOptions

What the site asks of the answer, in ChatRequest.options.

Prop

Type

maxTokens, temperature, topP and stop are hints: the account's connector leaves out those its provider refuses. toolChoice and responseFormat always reach the provider.

GenerationHint

The names of the hints, each with the type of its option:

Prop

Type

ToolChoice

Whether the model may answer in text, must call a tool, or must call the named one. A tool choice is one of these strings:

Prop

Type

Or an object that names the tool:

Prop

Type

ResponseFormat

An answer in JSON, held to schema when one is given. strict asks the providers that offer it to refuse any schema they cannot enforce exactly.

Prop

Type

const { message, finishReason } = await state.session.chat({
  messages: [{ role: 'user', content: 'Three calm books, as JSON.' }],
  options: {
    maxTokens: 400,
    toolChoice: 'none',
    responseFormat: {
      type: 'json',
      name: 'books',
      schema: { type: 'object', properties: { titles: { type: 'array', items: { type: 'string' } } } },
    },
  },
});
const { titles } = JSON.parse(message.content) as { titles: string[] };

// The hints a ChatGPT plan account refuses, which its connector leaves out of every call.
const refusedByChatGpt: GenerationHint[] = ['maxTokens', 'temperature', 'topP'];

Session types

A ProviderSession answers chats. On a site it is the wallet answering with the visitor's account: the session of a connected state, with providerId set to aiwallet and model set to the account's default model, or an empty string with Auto, where the request's onStart names the account and model of each answer. Its chat sends one request to the wallet and resolves with the answer.

ProviderSession

Prop

Type

ChatRequest

Prop

Type

onStart receives who answers, once, before the first text or the result: the account's display name and the model the wallet asks it for. A site on Auto learns there which account the wallet picked. onText receives the answer's text as it streams. Aborting signal stops the chat in the wallet.

ChatStart

Prop

Type

ChatResponse

Prop

Type

model is the model that answered, as the provider named it. usage is absent when the provider reports no token counts.

FinishReason

Why the answer ended: it was complete, it reached the token limit, it calls tools, or the provider's filter stopped it.

Prop

Type

Usage

The tokens the provider counted for the call. inputTokens includes cachedInputTokens.

Prop

Type

const { state } = client;
if (state.status === 'connected') {
  const response: ChatResponse = await state.session.chat({
    messages: [{ role: 'user', content: 'Summarise this page in two sentences.' }],
    onText: (delta) => output.append(delta),
  });
  if (response.finishReason === 'length') {
    output.append(' (cut at the token limit)');
  }
  const usage: Usage | undefined = response.usage;
  console.log(response.model, usage?.inputTokens, usage?.outputTokens);
}

What a site sees of the wallet: whether it may use the visitor's AI, and then which account answers it and on which model. AiConnect builds its ConnectionState from the links the wallet returns and announces. You meet this type directly when you implement the wallet protocol.

A site that is not connected:

Prop

Type

A connected site:

Prop

Type

function describe(link: WalletLink): string {
  return link.connected ? `${link.providerName} answers this site` : 'Not connected';
}

WalletProvider

The wallet as a page sees it: window.aiwallet, which the extension announces to every page. The wallet protocol specifies its methods.

Prop

Type

A chat streams its events to onEvent: one start naming who answers, then each piece of text. Aborting signal rejects the request with the signal's reason, and the wallet stops the chat it runs for it. onLink registers a listener for the visitor's changes, in the wallet, to the account or model the site answers with, and returns a function that removes it.

WalletChatParams

What a site sends with a chat request.

Prop

Type

A wallet of your own for a page's tests, set before the page creates its AiConnect:

const testWallet: WalletProvider = {
  isAIWallet: true,
  request: async (method, params, onEvent) => {
    if (method === 'status' || method === 'connect') {
      return { connected: true, providerName: 'Test' } satisfies WalletLink;
    }
    if (method === 'disconnect') {
      return { connected: false } satisfies WalletLink;
    }
    if (method === 'chat') {
      const { messages } = params as WalletChatParams;
      const content = `You said: ${textOf(messages.at(-1)?.content ?? '')}`;
      onEvent?.({ kind: 'start', providerName: 'Test', model: 'test' });
      onEvent?.({ kind: 'text', delta: content });
      return { message: { role: 'assistant', content }, finishReason: 'stop', model: 'test' } satisfies ChatResponse;
    }
    throw new WalletRequestError(`Unknown method ${method}`, 400);
  },
  onLink: () => () => undefined,
};

Object.assign(window, { aiwallet: testWallet });

ChatEvent

What a chat streams to the page before its result, in order: one start, sent before the first text or, when the answer has no text, before the result, then a text event per piece of text. The wallet protocol describes them.

Prop

Type

On this page