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

@resolvekit/nextjs

v1.0.4

Published

ResolveKit Next.js SDK with runtime parity and browser tool packs

Downloads

530

Readme

@resolvekit/nextjs

Universal Next.js SDK for ResolveKit

Install

npm install @resolvekit/nextjs

1) Add Secure Server Routes (App Router)

app/api/resolvekit/client-token/route.ts

import { createResolveKitClientTokenHandler } from "@resolvekit/nextjs/server";

const handler = createResolveKitClientTokenHandler({
  agentBaseUrl: process.env.RESOLVEKIT_AGENT_URL ?? "https://agent.resolvekit.app",
  resolveApiKey: () => process.env.RESOLVEKIT_API_KEY,
  authorizeRequest: async ({ request }) => {
    const origin = request.headers.get("origin");
    return origin === "https://app.example.com";
  },
  allowedOrigins: ["https://app.example.com"],
  // allowMissingOrigin: true // optional for trusted server-to-server traffic
  // forwardOriginHeader: false // optional for local dev if key origin allowlist blocks localhost
});

export const POST = handler;

app/api/resolvekit/actions/route.ts

import { createResolveKitActionsProxyHandler } from "@resolvekit/nextjs/server";

const handler = createResolveKitActionsProxyHandler({
  targetBaseUrl: process.env.APP_BACKEND_URL ?? "http://localhost:8000",
  allowlist: [
    { path: "/v1/workflows/run", methods: ["POST"] },
    { path: "/v1/automation/trigger", methods: ["POST"] }
  ],
  authorizeRequest: async ({ request }) => {
    const origin = request.headers.get("origin");
    return origin === "https://app.example.com";
  },
  allowedOrigins: ["https://app.example.com"],
  forwardRequestHeaders: ["x-request-id"],
  resolveAuthHeader: () => {
    const token = process.env.APP_BACKEND_SERVICE_TOKEN;
    return token ? `Bearer ${token}` : undefined;
  }
});

export const POST = handler;

2) Configure Runtime (Client Component)

"use client";

import {
  ResolveKitAction,
  ResolveKitDevtools,
  ResolveKitProvider,
  ResolveKitChat,
  ResolveKitWidget,
  createClientTokenAuthProvider,
  createRecommendedBrowserToolsPack,
  type ResolveKitConfiguration
} from "@resolvekit/nextjs";

const authProvider = createClientTokenAuthProvider({
  endpoint: "/api/resolvekit/client-token"
});

const browserTools = createRecommendedBrowserToolsPack({
  apiEndpoint: "/api/resolvekit/actions"
});

const config: ResolveKitConfiguration = {
  baseUrl: process.env.NEXT_PUBLIC_RESOLVEKIT_AGENT_URL ?? "https://agent.resolvekit.app",
  sdkVersion: "1.0.0",
  authProvider,
  functions: [
    ...browserTools.functions,
    {
      name: "get_local_time",
      description: "Returns current local time",
      parametersSchema: { type: "object", properties: {}, required: [] },
      requiresApproval: true,
      invoke: async () => ({ now: new Date().toISOString() })
    }
  ]
};

export default function ResolveKitHost() {
  return (
    <ResolveKitProvider configuration={config}>
      <ResolveKitWidget />
      <ResolveKitDevtools
        browserToolsOptions={{
          discoveryMode: "annotatedOnly",
          apiEndpoint: "/api/resolvekit/actions"
        }}
      />
      {/* Or render the headless/inline component if you need full custom layout */}
      {/* <ResolveKitChat /> */}
    </ResolveKitProvider>
  );
}

ResolveKitWidget ships as the default polished chat surface:

  • Floating launcher with a Figma-style popup panel
  • Theme defaults to system (inherits OS preference on first load)
  • Optional single-click toggle to switch to the opposite theme (light <-> dark)
  • Uses backend chat theme tokens (iOS parity)
  • Inherits chat name and message placeholder from backend session/localization
  • Shows typing bubbles while waiting for assistant response
  • Renders suggested reply chips, richer tool-approval cards, and disabled attachment affordance
  • Uses backend-provided tool-call explanations in approval cards
  • Renders in a Shadow DOM root to isolate SDK styles from host app global CSS

ResolveKitChat now uses the same visual system as the widget, rendered inline inside its own Shadow DOM host so host page global styles cannot override it.

Key props:

  • showThemeToggle?: boolean (default: true)
  • launcherLabel?: string (default: Need help?)
  • position?: "bottom-right" | "bottom-left" (default: "bottom-right")

Developer-Friendly Browser Tools

The recommended setup is now:

  • createRecommendedBrowserToolsPack(...) for secure defaults
  • ResolveKitAction or useResolveKitActionProps(...) to register UI affordances
  • ResolveKitDevtools during development so you can see what the agent can currently discover

The recommended helper only exposes discovery-based browser actions plus call_backend_api. If you explicitly need raw selector tools like highlight_element, click_element, or navigate_route, use createBrowserToolsPack(...) instead.

React Registration Helpers

Use ResolveKitAction when the SDK owns the element:

<ResolveKitAction
  as="button"
  actionId="open-billing-modal"
  actionRole="dialog-trigger"
  description="Open the billing modal"
  type="button"
>
  Open billing
</ResolveKitAction>

Use useResolveKitActionProps(...) when you already have a component and only want props to spread:

const billingLinkProps = useResolveKitActionProps({
  actionId: "go-to-billing",
  actionRole: "navigation",
  description: "Navigate to billing"
});

<Link {...billingLinkProps} href="/billing">
  Billing
</Link>

Dev Overlay

Use ResolveKitDevtools in development to inspect the currently registered actions and routes:

<ResolveKitDevtools
  defaultOpen
  browserToolsOptions={{
    discoveryMode: "annotatedOnly",
    apiEndpoint: "/api/resolvekit/actions"
  }}
/>

The overlay shows:

  • current discovery mode
  • current route
  • currently discoverable registered actions
  • currently discoverable registered routes

Registration Modes

createBrowserToolsPack() supports two discovery modes:

  • discoveryMode: "open": default. The SDK can discover common visible controls such as buttons, links, and inputs.
  • discoveryMode: "annotatedOnly": hardened mode. The SDK only discovers and acts on elements or routes the app explicitly marks.

Recommended hardened setup:

const browserTools = createRecommendedBrowserToolsPack({
  apiEndpoint: "/api/resolvekit/actions",
  // discoveryMode defaults to "annotatedOnly"
});

In annotatedOnly mode:

  • discover_page_actions only returns marked elements and links
  • click_discovered_action, highlight_discovered_action, and navigate_discovered_route only work on discovered ids from those marked elements
  • raw click_element and highlight_element reject unannotated selectors
  • raw navigate_route rejects routes that are not registered by an annotated link on the current page

Supported annotation attributes:

  • data-resolvekit-id: stable developer-owned identifier and preferred selector anchor
  • data-resolvekit-role: optional semantic hint for tooling and filtering
  • data-resolvekit-description: optional human hint

Example:

<button
  data-resolvekit-id="open-billing-modal"
  data-resolvekit-role="dialog-trigger"
  data-resolvekit-description="Open the billing modal"
>
  Open billing
</button>

<a
  data-resolvekit-id="go-to-billing"
  data-resolvekit-role="navigation"
  href="/billing"
>
  Billing
</a>

By default, discovery does not return data-resolvekit-description text back to the model. If you want that richer context, opt in explicitly:

const browserTools = createBrowserToolsPack({
  apiEndpoint: "/api/resolvekit/actions",
  includeElementDescriptionsInDiscovery: true
});

3) Custom UI (Headless)

"use client";

import { ResolveKitProvider, useResolveKit, type ResolveKitConfiguration } from "@resolvekit/nextjs";

function CustomChat() {
  const { state, sendMessage, approveToolCallBatch, declineToolCallBatch } = useResolveKit();

  return (
    <div>
      <h2>{state.chatTitle}</h2>
      <button onClick={() => void sendMessage("Hello")}>Send</button>
      {state.toolCallBatchState === "awaitingApproval" ? (
        <>
          <button onClick={() => void approveToolCallBatch()}>Approve</button>
          <button onClick={() => void declineToolCallBatch()}>Decline</button>
        </>
      ) : null}
    </div>
  );
}

export function ChatShell({ config }: { config: ResolveKitConfiguration }) {
  return (
    <ResolveKitProvider configuration={config}>
      <CustomChat />
    </ResolveKitProvider>
  );
}

Security Defaults

  • API key stays server-side.
  • Browser receives short-lived client token from /api/resolvekit/client-token.
  • Backend action tool is routed through an allowlisted proxy endpoint.
  • Server helpers require authorizeRequest and allowedOrigins in production.
  • Browser tools that mutate state remain approval-required; read-only discovery and highlight tools do not.
  • Use discoveryMode: "annotatedOnly" if you want the browser agent limited to developer-registered UI affordances only.
  • createRecommendedBrowserToolsPack(...) makes that hardened mode the easiest starting point.
  • DOM-derived freeform descriptions are excluded from discovery by default; opt in with includeElementDescriptionsInDiscovery: true only when you explicitly want that extra model context.
  • Device IDs stay in memory by default. Opt into deviceIdPersistence: "localStorage" only if you want sticky client identity.

Transport

The SDK now communicates with ResolveKit over an HTTP session stream:

Client -> Server: POST /v1/sessions/{id}/messages | POST /v1/sessions/{id}/tool-results
Server -> Client: GET /v1/sessions/{id}/events

The browser runtime and server transport share the same streamed HTTP protocol, so they can use HTTP/3 when the platform and backend negotiate it.

For server or SSR flows that need direct access to the transport layer, use createResolveKitServerTransport from @resolvekit/nextjs/server.

Scripts

npm run test:run
npm run typecheck
npm run build

Example App

See examples/next-app for a complete App Router integration that includes:

  • ResolveKitAction and useResolveKitActionProps(...) registration helpers
  • ResolveKitDevtools showing the current discoverable action surface
  • discovery-first browser action demos
  • data-resolvekit-* annotations for developer-registered browser actions
  • discovery-based highlighting and clicking against registered actions
  • route navigation between / and /billing
  • button click automation to open/close modal flows
  • backend action calls via /api/resolvekit/actions to /api/demo/workflows/run