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

@shelf/agent-experience-sdk

v1.0.1

Published

SDK for embedding Shelf Agent Experience into host platforms.

Readme

Agent Experience SDK

The Agent Experience SDK embeds Shelf Agent Experience into an external agent desktop. It renders the AX widget iframe, passes conversation state into the widget, and exposes typed methods and events for host-side integrations.

Installation

pnpm add @shelf/agent-experience-sdk
import {AX_SDK} from '@shelf/agent-experience-sdk';
import type {Message, SDKConfig, WidgetEvents} from '@shelf/agent-experience-sdk';

Quick Start

const config = {
    baseUrl: 'https://{subdomain}.shelf.io',
    configId: 'agent-experience-default',
    telemetry: {
        system: 'genesys',
        location: 'conversation_widget',
        conversationType: 'chat',
    },
    conversation: {
        conversationId: 'conv-123',
        state: 'open',
    },
    messages: [
        {
            id: 'msg-1',
            type: 'customer',
            text: 'I need help with my order',
            createdAt: '2026-05-07T12:00:00.000Z',
            origin: 'history',
        },
    ],
    context: {
        locale: 'en-US',
        customerTier: 'enterprise',
    },
} satisfies SDKConfig;

await AX_SDK.render(config, document.getElementById('ax-widget')!);

AX_SDK.on('reply:selected', payload => {
    console.log(payload.text);
});

await AX_SDK.appendMessages([
    {
        id: 'msg-2',
        type: 'agent',
        text: 'I can help with that.',
        origin: 'realtime',
    },
]);

AX_SDK.close();

Configuration

render(config, container) accepts an SDKConfig object.

Required Fields

| Field | Type | Description | |-------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------| | baseUrl | string | Shelf Cortex origin or widget URL. For example, https://{account}.shelf.io
| configId | string | Published Agent Experience configuration id to load. This is fixed for one rendered session. Close and render again to switch configs. | | telemetry | Telemetry | Host platform and channel metadata used by AX telemetry and runtime behavior. |

Optional Fields

| Field | Type | Description | |----------------|---------------------------|------------------------------------------------------------------------------| | conversation | Conversation | Initial conversation identity and state. | | messages | Message[] | Initial transcript. Missing origin values are treated as history. | | context | Record<string, unknown> | Runtime context such as locale, account, queue, brand, or customer metadata. || | settings | SDKSettings | Optional host-controlled intelligence toggles. |

Telemetry

type Telemetry = {
    system: 'genesys' | 'kustomer' | '...'; // Host platform or system name integrating the AX
    conversationType: 'chat' | 'voice' | 'email' | 'messaging';
};

Conversation

type Conversation = {
    conversationId?: string;
    state?: 'closed' | 'open';
};

Messages

type Message = {
    id: string;
    type: 'customer' | 'agent';
    text: string;
    createdAt?: string;
    createdByUser?: {
        id: string;
        name?: string;
    };
    origin: 'history' | 'realtime';
};

Use origin: 'history' for transcript messages that already existed before the widget was rendered. Use origin: 'realtime' for live messages appended after render.

Settings

type SDKSettings = {
    intelligence?: {
        manualSummaryTriggerEnabled?: boolean;
        replySelectionEnabled?: boolean;
    };
};

manualSummaryTriggerEnabled controls whether the host allows manual summary requests. replySelectionEnabled controls whether reply selection events should be enabled for the current session.

Methods

AX_SDK.render(config, container)

Renders the AX widget iframe into an HTMLElement or selector string. The method resolves after the underlying iframe render completes; it does not wait for the widget ready event.

await AX_SDK.render(config, '#ax-widget');

Only one widget can be rendered by this singleton SDK at a time. Call AX_SDK.close() before rendering a new session.

AX_SDK.setConversation(conversation)

Replaces the current conversation object and sends the updated props to the widget.

await AX_SDK.setConversation({
    conversationId: 'conv-456',
    state: 'transferred',
});

Use this when the host moves the mounted widget to another conversation or when conversation state changes.

AX_SDK.appendMessages(messages)

Appends live messages to the current message list and sends the full normalized list to the widget. Missing origin values are treated as realtime.

await AX_SDK.appendMessages([
    {
        id: 'msg-3',
        type: 'customer',
        text: 'Can you resend the invoice?',
        origin: 'realtime',
    },
]);

Use render(..., {messages}) or setConversation(...) for transcript replacement. Use appendMessages(...) for incremental live updates.

AX_SDK.setContext(context)

Replaces the runtime context object and sends it to the widget.

await AX_SDK.setContext({
    locale: 'en-US',
    queue: 'billing',
    customerTier: 'enterprise',
});

Context values must be serializable across iframe boundaries.

AX_SDK.generateSummary()

Requests summary generation inside the widget

const summary = await AX_SDK.generateSummary();

AX_SDK.detectWrapupCodes(codes)

Sends the available wrap-up codes to the widget for AI-assisted detection.

await AX_SDK.detectWrapupCodes([
    {
        id: 'refund',
        name: 'Refund requested',
        description: 'The customer asked for a refund or return.',
    },
]);

Each code must include id and name; description is optional.

AX_SDK.request(params)

Makes an authenticated Shelf API request through the widget session, using the currently logged-in AX user.

const user = await AX_SDK.request<{ name: string }>({
    method: 'GET',
    url: 'auth/v1/user',
});

Request params:

type RequestParams = {
    url: string;
    method: 'PATCH' | 'PUT' | 'DELETE' | 'GET' | 'POST';
    body?: Record<string, unknown> | BodyInit | null;
    headers?: Record<string, string>;
};

AX_SDK.on(eventName, handler)

Subscribes to a widget event and returns a cancellable subscription.

const subscription = AX_SDK.on('wrapup:selected', payload => {
    console.log(payload.code, payload.comment);
});

subscription.cancel();

Call render(...) before subscribing.

AX_SDK.once(eventName, handler)

Subscribes to one occurrence of a widget event and then removes the listener.

AX_SDK.once('ready', payload => {
    console.log(payload.widgetVersion);
});

The returned subscription can still be cancelled before the event fires.

AX_SDK.close()

Closes the widget instance, clears SDK state, and allows another render(...) call.

AX_SDK.close();

AX_SDK.version

Returns the SDK package version.

console.log(AX_SDK.version);

Events

| Event | Payload | Description | |----------------------|--------------------------------------------------------|--------------------------------------------------| | ready | {user, widgetVersion, protocolVersion, capabilities} | Widget runtime is ready. | | summary:requested | {} | User requested a summary from inside the widget. | | reply:selected | {text} | User selected a suggested reply. | | wrapup:selected | {id?, code, comment?} | User selected a wrap-up code. | | conversation-close | {summary, wrapupCode} | Widget emitted close-time conversation details. | | content:open | {href?, entityId?, label?} | User opened content from AX. | | error | {message} | Widget emitted an error event. |

Full Host Example

import {AX_SDK} from '@shelf/agent-experience-sdk';
import type {Message, SDKConfig} from '@shelf/agent-experience-sdk';

const container = document.querySelector<HTMLElement>('#ax-widget');

const initialMessages: Message[] = transcript.map(item => ({
    id: item.id,
    type: item.author === 'agent' ? 'agent' : 'customer',
    text: item.text,
    createdAt: item.createdAt,
    createdByUser: item.userId
        ? {
            id: item.userId,
            name: item.userName,
        }
        : undefined,
    origin: 'history',
}));

const config: SDKConfig = {
    baseUrl: 'https://{account}.shelf.io',
    configId: 'agent-experience-default',
    telemetry: {
        system: 'genesys',
        location: 'conversation_widget',
        conversationType: 'chat',
    },
    conversation: {
        conversationId: currentConversation.id,
        state: currentConversation.state,
    },
    messages: initialMessages,
    context: {
        locale: currentAgent.locale,
        queue: currentConversation.queueName,
        customerId: currentConversation.customerId,
    },
    settings: {
        intelligence: {
            manualSummaryTriggerEnabled: true,
            replySelectionEnabled: true,
        },
    },
};

await AX_SDK.render(config, container);

const subscriptions = [
    AX_SDK.on('ready', payload => {
        console.log('AX ready', payload.widgetVersion);
    }),
    AX_SDK.on('reply:selected', payload => {
        insertReplyIntoComposer(payload.text);
    }),
    AX_SDK.on('wrapup:selected', payload => {
        setWrapupCode(payload.code, payload.comment);
    }),
    AX_SDK.on('conversation-close', payload => {
        saveCloseDetails(payload.summary, payload.wrapupCode);
    }),
    AX_SDK.on('content:open', payload => {
        openKnowledgeContent(payload.href ?? payload.entityId);
    }),
    AX_SDK.on('error', payload => {
        console.error(payload.message);
    }),
];

hostConversation.onMessage(async message => {
    await AX_SDK.appendMessages([
        {
            id: message.id,
            type: message.author === 'agent' ? 'agent' : 'customer',
            text: message.text,
            createdAt: message.createdAt,
            origin: 'realtime',
        },
    ]);
});

hostConversation.onContextChange(async context => {
    await AX_SDK.setContext(context);
});

hostConversation.onClose(() => {
    subscriptions.forEach(subscription => subscription.cancel());
    AX_SDK.close();
});