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

@lens-os/sdk

v0.1.5

Published

Lens OS Frontend AI Agent SDK

Readme

@lens-os/sdk

AI Agent SDK for building conversational AI experiences in web applications.

npm version

Features

  • Server/Client Architecture - API keys stay on the server, frontend communicates via SSE
  • Multi-turn Agent Loop - Autonomous LLM orchestration with tool execution
  • Streaming Responses - Real-time text and tool call events via Server-Sent Events
  • React Hooks - useLensAgent and useChat for easy frontend integration
  • Tool System - 3-tier priority: Manual executors > Customer endpoints > Platform built-ins
  • Session Management - Conversation history with automatic memory compaction
  • Custom Tools - Define your own tool executors with full metadata for prompt generation
  • Debug Mode - Configurable logging via debug flag
  • TypeScript - Full type definitions included

Installation

npm install @lens-os/sdk
# or
bun add @lens-os/sdk

Peer dependencies:

  • openai >= 4.0.0 (required)
  • react >= 18.0.0 (optional, only for React hooks)

Architecture

Browser (React)                          Server (Next.js / Node)
┌──────────────────┐                    ┌──────────────────────┐
│  useLensAgent()  │── POST /agent ───> │  createAgentHandler  │
│                  │<── SSE stream ──── │    SupervisorAgent   │
│                  │── POST /action ──> │    (LLM + Tools)     │
└──────────────────┘                    └──────────────────────┘
  • Frontend sends messages and receives streamed responses
  • Server holds API keys, runs the agent loop, calls LLM and tools
  • Action requests allow the server to ask the client to perform DOM operations (click, scroll, navigate, etc.)

Quick Start

1. Server-side: Create API Route

// app/api/agent/route.ts (Next.js App Router)
import { createAgentHandler } from '@lens-os/sdk/server';

const handler = createAgentHandler({
  apiKey: process.env.LENS_API_KEY!,
  openaiKey: process.env.OPENAI_API_KEY!,
  model: 'gpt-4o',          // optional, default: gpt-4o
  debug: true,               // optional, enables SDK logging
});

export const POST = handler.POST;

2. Server-side: Create Action Result Route

// app/api/agent/action/route.ts
import { createActionResultHandler } from '@lens-os/sdk/server';

// Share the pendingActions store from the agent handler
import { handler } from '../route';

const actionHandler = createActionResultHandler(handler._pendingActions);
export const POST = actionHandler.POST;

3. Client-side: React Hook

import { useLensAgent } from '@lens-os/sdk/react';

function ChatWidget() {
  const {
    messages,
    isLoading,
    sendMessage,
    abort,
  } = useLensAgent({
    endpoint: '/api/agent',
  });

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i} className={msg.role}>
          {typeof msg.content === 'string' ? msg.content : '...'}
        </div>
      ))}
      <input
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            sendMessage(e.currentTarget.value);
            e.currentTarget.value = '';
          }
        }}
        placeholder="Type a message..."
        disabled={isLoading}
      />
      {isLoading && <button onClick={abort}>Stop</button>}
    </div>
  );
}

Entry Points

| Import path | Usage | |---|---| | @lens-os/sdk | Core types, LensClient, SupervisorAgent, utilities | | @lens-os/sdk/react | React hooks (useLensAgent, useChat) | | @lens-os/sdk/server | Server handler (createAgentHandler, createActionResultHandler) |

Server Configuration

import { createAgentHandler } from '@lens-os/sdk/server';

const handler = createAgentHandler({
  // Required
  apiKey: string,              // Lens OS API key
  openaiKey: string,           // OpenAI API key

  // Optional
  baseUrl?: string,            // Default: https://osapi.ask-lens.ai
  model?: string,              // Default: gpt-4o
  maxTurns?: number,           // Default: 10
  language?: 'zh-TW' | 'en-US',
  debug?: boolean,             // Enable SDK logging (default: false)
  actionTimeout?: number,      // Action request timeout ms (default: 30000)

  // Shared client (reuse across handlers to share config cache)
  client?: LensClient,

  // Callbacks
  onTrace?: (trace: LLMTrace) => void,

  // Custom tool executors
  toolExecutors?: Record<string, ToolExecutorFunction | ToolExecutorConfig>,
});

React Hooks

useLensAgent

Full-featured hook for building chat interfaces.

import { useLensAgent } from '@lens-os/sdk/react';

const {
  // State
  messages,           // Message[] - conversation history
  isLoading,          // boolean - agent is running
  sessionId,          // string - current session ID
  error,              // Error | null

  // Actions
  sendMessage,        // (message: string, context?) => Promise<void>
  abort,              // () => void - cancel current execution
  clearMessages,      // () => void - clear local messages
  newSession,         // () => void - start a new session
  loadSession,        // (sessionId: string, messages: Message[]) => void
} = useLensAgent({
  endpoint: '/api/agent',

  // Optional
  actionResultEndpoint?: string,    // Custom action result endpoint
  userId?: string,
  headers?: Record<string, string> | (() => Record<string, string>),
  onEvent?: (event: SSEEvent) => void,
  getPageState?: () => Promise<PageState>,
  onActionRequest?: (action: string, params: Record<string, any>) => Promise<ToolResult>,
});

useChat

Simplified wrapper with shorter method names.

import { useChat } from '@lens-os/sdk/react';

const {
  messages,
  isLoading,
  error,
  send,     // sendMessage
  stop,     // abort
  clear,    // clearMessages
  reset,    // newSession
} = useChat({
  endpoint: '/api/agent',
});

Custom Tool Executors

Define custom tools on the server. Tool metadata is automatically injected into the LLM prompt.

const handler = createAgentHandler({
  apiKey: '...',
  openaiKey: '...',
  toolExecutors: {
    // Simple function
    my_tool: async (params, context) => {
      return { success: true, result: 'done' };
    },

    // Full config with metadata (recommended)
    order_lookup: {
      description: 'Look up order details by order ID',
      whenToUse: 'User asks about order status or delivery',
      schema: {
        orderId: { type: 'string', required: true, description: 'The order ID' },
      },
      output: 'Order object with status, items, tracking info',
      execute: async (params, context) => {
        const order = await db.orders.findById(params.orderId);
        if (!order) {
          return { success: false, error: 'Order not found' };
        }
        return { success: true, result: order };
      },
    },
  },
});

Tool Execution Priority

When the LLM calls a tool, the SDK resolves it in this order:

  1. Manual toolExecutors - Code-defined executors (highest priority)
  2. CUSTOMER mode - Database-configured external endpoint (POST to customerEndpoint)
  3. PLATFORM mode - Built-in SDK implementations (knowledge_search, DOM tools, etc.)

Shared LensClient

When running multiple handlers or agents, share a LensClient instance to reuse the config cache (60s TTL with automatic retry):

import { LensClient } from '@lens-os/sdk';
import { createAgentHandler } from '@lens-os/sdk/server';

const sharedClient = new LensClient({
  apiKey: process.env.LENS_API_KEY!,
  baseUrl: 'https://osapi.ask-lens.ai',
});

const handler = createAgentHandler({
  apiKey: process.env.LENS_API_KEY!,
  openaiKey: process.env.OPENAI_API_KEY!,
  client: sharedClient,  // Reuses config cache
});

Debug Mode

By default, the SDK produces no console output. Enable debug logging:

// Via config
const handler = createAgentHandler({
  apiKey: '...', openaiKey: '...',
  debug: true,
});

// Or manually
import { setDebug } from '@lens-os/sdk';
setDebug(true);

SSE Event Types

The server streams these events to the client:

| Event type | Description | |---|---| | text | Streamed text content from the LLM | | tool_call | LLM is invoking a tool (name + parameters) | | tool_result | Tool execution result | | action_request | Server asks client to perform a DOM action | | error | Error occurred (may be fatal) | | done | Agent execution complete |

Page State & DOM Actions

The SDK supports multimodal context. Send page state with messages:

sendMessage('What products are on this page?', {
  pageState: {
    url: window.location.href,
    title: document.title,
    markdown: '...',           // Page content as markdown
    screenshot: 'data:...',    // Base64 screenshot
    actionableElements: [],    // Clickable elements
  },
});

The server can request DOM actions on the client (click, scroll, navigate, etc.) via the action request protocol. Handle them with onActionRequest:

useLensAgent({
  endpoint: '/api/agent',
  onActionRequest: async (action, params) => {
    // Execute DOM action and return result
    return { success: true, result: 'clicked' };
  },
});

Requirements

  • Node.js >= 18
  • React >= 18 (optional, for React hooks)
  • OpenAI API key
  • Lens OS API key

License

MIT