@tansr/sdk · Node.js ≥ 22.19 · ESM · TypeScript types · UI framework independent

Agent Harness SDK for your application

Tansr AI Agent SDK brings an agent runtime into your product. Model calls, tool execution, conversation context and permission checks share one kernel. You own the business logic, interface and identity system; the SDK runs each request through to a result your application can handle.

Example baseline: @tansr/sdk 0.11.1, the current npm release. The install command pins this version. Version 0.12.0 has not been released.

Install the TypeScript SDK
npm install @tansr/sdk@0.11.1

An Agent Harness SDK, beyond a model request

An Agent Loop SDK handles the model-to-tool cycle: request a tool, return its result to the context, and continue until completion, interruption or a runtime limit. Tansr's Harness also supplies tool scheduling, permission decisions, context compaction, session storage interfaces and view projections. It is more than a wrapper around a single model HTTP request.

  1. 01Receive user input
  2. 02Call the configured model
  3. 03Check permissions and run tools
  4. 04Return results and continue
  5. 05Complete, cancel or reach a limit
query(options)
A single-turn API for Node.js scripts and individual tasks. Its async generator emits events and returns final text, conversation history and usage as its completion value.
createSession(options)
A multi-turn session with send, events, idle, interrupt, setModel and close. Use it for desktop assistants and conversations inside your product.
runAgent(options)
A lower-level API for applications that supply their own model client, tools and executor and want to drive the kernel directly.

Quickstart: install, issue a token, stream a session

These examples use the published @tansr/sdk 0.11.1 API and platform token mode for distributed applications. Run the SDK in Node.js or the Electron main process, not as a full runtime inside a browser renderer. TypeScript projects should target ES2022 or later.

Protect client credentials with short-lived tokens

  1. Create an application and configure its models and capabilities in the console. Keep both app key headers on your backend, outside client bundles, logs and source control.
  2. Authenticate the user on your own backend. Derive a stable endUserId from the verified session before requesting a platform token; do not trust an arbitrary user identifier in the client request.
  3. Return only token and expiresAt to the client. Renew through your login flow after expiry. Per-user revocation, disabling an application and rotating its app key provide additional ways to cut off access.
POST /v1/app-tokens
POST {platformBaseUrl}/v1/app-tokens
x-tansr-key-id: <server-side app key id>
x-tansr-key: <server-side app key>
Content-Type: application/json

{ "endUserId": "<verified-user-id>", "ttlSeconds": 3600 }

// Forward only { token, expiresAt } to your client.

This is the exchange interface, not a complete authentication service. Add TWP request signatures as required by your gateway deployment; see the server-side token example in the docs. Token TTL is 60–86400 seconds. Your application implements login, token handling and renewal.

Stream events and close the session explicitly

app.ts
import { createSession } from '@tansr/sdk';

const session = await createSession({ token, baseUrl });
const output = (async () => {
  for await (const event of session.events) {
    if (event.type === 'msg.text.delta') {
      process.stdout.write(event.text);
    }
  }
})();

session.send('Summarize this contract');
await session.idle();
session.send('List the points that need human review');
await session.idle();
session.close();
await output;

Your application supplies token, baseUrl, db, workspaceDir, storage paths and UI callbacks used in these examples. Subscribe before sending input. Keep long-lived conversations open and close them when the user finishes or the host shuts down.

Connect agent capabilities to real application work

Compose tools, MCP, Skills, files, sessions and UI around the same runtime. Selecting a tool does not grant permission: every example remains subject to application capabilities and runtime checks.

Custom tools and function calling with defineTool

Implement the execution side of AI tool calling with your own business functions. Parameter definitions generate the model-facing schema; handlers can query orders, tickets or internal data. Enforce tenant and user authorization inside those functions.

app.ts
import { createSession, defineTool } from '@tansr/sdk';

const searchOrders = defineTool({
  name: 'searchOrders',
  description: 'Search orders for the authenticated user',
  parameters: { keyword: { type: 'string' } },
  readOnly: true,
  handler: async ({ keyword }) =>
    db.searchForUser(verifiedUserId, keyword),
});

const session = await createSession({
  token, baseUrl,
  tools: { builtin: [], custom: [searchOrders] },
});

readOnly describes tool effects; it does not bypass authorization. Handler exceptions become structured tool errors. Keep credentials and sensitive internal error details out of results.

MCP tool integration over stdio and HTTP

Attach existing MCP services for knowledge and business systems. Configure a toolAllowlist per server and choose lazy discovery or eager loading. Connections can belong to one session or be shared by the application.

app.ts
import { createMcpHost, createSession } from '@tansr/sdk';

const host = createMcpHost({
  servers: {
    local: { transport: 'stdio', command: 'node',
      args: [mcpServerEntry] },
    knowledge: { transport: 'http',
      url: 'https://mcp.example.com/mcp',
      toolAllowlist: ['search'] },
  },
});
const session = await createSession({ token, baseUrl, mcp: host });
// Reuse host across sessions; dispose when the application exits.
// await host.dispose();

An allowlist limits exposed tools; it does not replace runtime approval. MCP calls are treated conservatively as third-party operations. Configure explicit permission rules or askUser. Closing a session does not dispose of a shared host.

Local file processing for an AI file assistant

Read files, list directories, search with Glob/Grep, and enable authorized writes or edits for document assistants, project analysis and desktop workflows. Choose the tools your application needs, then decide which operations require confirmation.

app.ts
const session = await createSession({
  token, baseUrl,
  cwd: workspaceDir,
  tools: { builtin: ['read', 'glob', 'grep', 'list'] },
});

write, edit and shell can be explicitly selected when authorized. cwd sets a working directory, not an operating-system sandbox. The host must still constrain process privileges and access to data.

Agent Skills for business knowledge and procedures

Use defineSkill for inline instructions or dirs for explicitly supplied SKILL.md directories. A brief index enters context and detailed instructions load on demand, so a business process need not place every document in every prompt.

app.ts
import { createSession, defineSkill } from '@tansr/sdk';

const review = defineSkill({
  name: 'contract-review',
  description: 'Contract review procedure',
  whenToUse: 'When the user asks to review contract risks',
  instructions: '# Review\nList facts, then flag items for human review.',
});
const session = await createSession({
  token, baseUrl,
  skills: { custom: [review], dirs: ['./assets/skills'] },
});

The SDK does not scan an end user's skill directories automatically. Skills provide knowledge and guidance; they grant no extra permissions and do not guarantee correct model output. Enable the application's skills capability first.

Streaming AI output and tool execution states

createSessionView projects text deltas, tool execution states and usage into immutable UI state for React or another interface. createNarrator can produce readable logs alongside it. AgentSession.events gives each subscriber an independent cursor.

app.ts
import { createSessionView, createNarrator } from '@tansr/sdk';

const view = createSessionView(session, {
  delivery: { text: 'stream', thinking: 'off' },
});
const unsubscribe = view.subscribe(state => render(state));
const narrator = createNarrator(session.events, {
  verbosity: 'normal', onLine: line => logger.info(line),
});

view.appendUserMessage(text);
session.send(text);
// Host teardown: unsubscribe(); view.dispose(); narrator.dispose();

Attach subscribers before sending prompts; a late subscriber should not expect a complete replay. Add user messages to the view explicitly. Hiding thinking output does not stop the model from generating or charging for thinking tokens.

Electron AI integration through the main process

For a desktop application AI SDK, run the session and tools in Electron's main process and keep the token there. Send projected state to the renderer; expose a narrow preload bridge for user input and approval responses.

main.ts
// Electron main process: window is your BrowserWindow.
const session = await createSession({ token, baseUrl });
const view = createSessionView(session);
const unsubscribe = view.subscribe(state => {
  window.webContents.send('agent:state', state);
});
// Handle input via a narrow IPC/preload bridge.
// Validate the sender and input before session.send(text).

See the Electron main process, preload and approval bridge examples in the docs. Electron must satisfy the SDK's Node requirement; the example uses Electron ≥39. Do not expose app keys, unrestricted filesystem access or arbitrary execution to the renderer.

Multi-turn conversations, session storage and resume

Start with in-memory agent session management, then inject a SessionStore. createFileSessionStore supplies a filesystem implementation; custom stores can implement the same interface. Loading history and continuing to save it are separate options.

app.ts
import { createFileSessionStore, createSession } from '@tansr/sdk';

const store = createFileSessionStore({ dir: userDataDir });
const session = await createSession({ token, baseUrl, store });
session.send('Remember the requirements for this task');
await session.idle();
const sessionId = session.sessionId;
session.close();

const resumed = await createSession({
  token, baseUrl,
  resume: { sessionId, store },
  store, // Continue persisting the resumed conversation.
});

Sessions stay in memory when no store or persistence callback is supplied. initialMessages and onHistoryCommit can integrate an existing database. When meta.rewritten is true, replace stored history instead of appending only its tail. The host owns storage authorization, encryption and retention.

Context compaction and restorable checkpoints

The runtime supports automatic compaction when the model's context window is known, plus explicit compact, checkpoint and restore operations. Long tasks can save a context checkpoint and later restore it or continue on a branch.

app.ts
// A store with checkpoints has been wired into session.
await session.idle();
const mark = await session.checkpoint({ label: 'before-summary' });
const result = await session.compact({
  instructions: 'Preserve task constraints and unfinished work',
});
if (result.status === 'compacted') {
  console.log(result.compactionId);
}
const restored = await session.restore(mark.checkpointId);
console.log(restored.status);

Compaction is a lossy summary, not a promise to remember every detail forever. Restoring context does not undo file writes, network requests or external business actions. Keep critical facts in your trusted application storage.

Multi-model integration and session model switching

Platform token mode uses the application's authorized model directory and switches subsequent turns through available aliases. Node.js services can also use locally configured managed mode; tests and special integrations can inject a client and model together.

app.ts
const session = await createSession({ token, baseUrl });
// Use an alias/handle available in this app's authorized bundle.
session.setModel('main');
session.send('Continue using the newly selected model');

An arbitrary vendor model name is not enough to obtain access. setModel affects subsequent turns, not a request already running. Input modalities, context windows and output capabilities depend on the selected model.

System tools for images, video, speech and search

imageGen, videoGen, speechToText and textToSpeech are kernel tools. New integrations select them through tools.builtin. SDK token mode uses platform providers; tool selection and capability authorization remain separate checks.

app.ts
const session = await createSession({
  token, baseUrl,
  tools: { builtin: [
    'imageGen', 'videoGen', 'speechToText', 'textToSpeech',
  ] },
});

Select media tools explicitly; they do not enter the default tool set. They also need platform access, authorized models and enabled capabilities. tools.platform is a compatibility alias. webSearch belongs in builtin too and requires both its tool capability and platform search channel; the SDK supplies no BYO search backend.

Agent permissions and tool-call approval

Application capabilities decide what can be assembled; runtime rules decide whether a specific call may execute. askUser routes confirmation to your interface. Platform token mode can also consult the application's appointed adjudicator.

app.ts
const session = await createSession({
  token, baseUrl,
  permission: {
    rules: {
      deny: ['Shell'],
      ask: ['Write', 'Edit', 'mcp__knowledge__*'],
    },
    askUser: async (call, signal) => {
      const approved = await confirmTool(call.name, call.args, signal);
      return approved ? 'allow' : 'deny';
    },
  },
});

Calls that still require confirmation do not execute by default without a callback. In 0.11.1, an explicit permission.mode conflicts with an appointed adjudicator, so this example omits mode. Do not rely on the unreleased 0.12.0 compatibility change. Approval is not OS isolation and does not replace business authorization.

Separate credentials, application capabilities and execution permissions

The application supplies authorization

In token mode, models, tool capabilities and governance settings come from the owning application. System tool selection and platform authorization are separate dimensions: media tools use the builtin interface but still depend on platform capability settings. Selecting an unauthorized tool produces an assembly error. Availability depends on application configuration and the installed SDK version.

Permissions apply to individual calls

File operations, custom functions and MCP calls still receive runtime decisions. Read-only declarations, MCP allowlists, user confirmation and business data authorization solve different problems. External messages, database writes and financial actions require your application's own authority and confirmation rules.

No upstream model keys on the client

In platform token mode, provider credentials stay on the platform, app keys stay on the developer backend, and the client holds a short-lived app_user token. The token is still a credential: your product remains responsible for login checks, renewal and safe client-side handling.

AI usage statistics, token accounting and per-user reconciliation

Live feedback inside your interface

cost.usage.updated reports usage after model requests, and SessionView can aggregate it for display. This supports in-product feedback; server-side records remain authoritative for prices and settlement.

Users query their own counts

GET /v1/my-usage uses a short-lived token and is scoped to its endUserId. The response contains no monetary fields. Changing a user identifier in a query does not grant access to another user's usage.

You implement end-user billing

/v1/app-usage/by-end-user is the developer's per-user financial reconciliation endpoint; do not proxy it unchanged to clients. Distinguish SDK plans, model/media usage charges and what your own product charges its users. End-user pricing and payment flows belong to your business.

usage.ts
for await (const event of session.events) {
  if (event.type === 'cost.usage.updated') {
    updateUsage(event);
  }
}
// Subscribe before sending prompts; keep financial reconciliation server-side.