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

@mobilabsolutions/axium-agent-sdk

v1.2.0

Published

https://mobilabsolutions.com/axium/

Readme

@mobilabsolutions/axium-agent-sdk

https://mobilabsolutions.com/axium/

Installation

npm install @mobilabsolutions/axium-agent-sdk

Styles

The SDK ships two stylesheet variants:

Tailwindcss

If your host app already uses Tailwind:

import '@mobilabsolutions/axium-agent-sdk/styles.css';

This variant uses CSS @layer's and does not include Tailwind's preflight, since the host app should already be providing it.

Unlayered styles

If your host app uses a vanilla CSS reset or no CSS framework:

import '@mobilabsolutions/axium-agent-sdk/styles-unlayered.css';

This variant has no @layers defined, and it assumes the host app already has its own reset/preflight in place. It's important that the styles be imported next to the host styles in order for the host to take precedence.

More styling control

| Prop | Type | Default | Description | | ------- | ------- | ------- | ----------------------------------------------------------------- | | theme | Theme | — | Optional theme object (colors, typography, logos, favicon, fonts) |

Theme object

All fields are optional. Only the fields you provide will be applied.

import type { Theme } from '@mobilabsolutions/axium-agent-sdk';

const theme: Theme = {
  title: 'My App',
  tokens: {
    colors: {
      // Overrides CSS variables
      'primary-A400': '#0066cc',
      'primary-A500': '#004d99',
    },
    typography: {
      primary: 'Roboto',
      secondary: 'Lato',
    },
  },
};

Usage

import {
  AxiumAgentProvider,
  Chat,
  BlankChat,
  useChats,
  useChatLoader,
  useCreateChat,
  useDeleteChat,
} from '@mobilabsolutions/axium-agent-sdk';
import '@mobilabsolutions/axium-agent-sdk/styles.css';

function App() {
  return (
    <AxiumAgentProvider
      baseUrl="https://agent.axium.com"
      msalInstance={msal}
      scopes={['api://<agent-client-id>/.default']}
      theme={theme}
    >
      <ChatPane />
    </AxiumAgentProvider>
  );
}

function ChatPane() {
  const { data: chats } = useChats();
  const { chat, isLoading } = useChatLoader(chatId);
  const { create: createChat, creating } = useCreateChat({
    done: (chatId) => {
      /* navigate to chat */
    },
  });
  const { mutate: deleteChat } = useDeleteChat();

  if (isLoading) return <div>Loading...</div>;
  if (!chat) return <BlankChat onSubmit={handleSubmit} hideConnectors />;
  return <Chat chat={chat} hideConnectors />;
}

Props

AxiumAgentProvider

| Prop | Type | Default | Description | | -------------- | -------------------------- | -------- | -------------------------------------------------------------------------------------------------- | | baseUrl | string | required | Base URL for API calls (e.g. "https://agent.axium.com") | | msalInstance | IPublicClientApplication | required | MSAL instance for Bearer token auth | | scopes | string[] | required | Token scopes (e.g. ["api://<agent-client-id>/.default"]) | | theme | Theme | — | Optional theme object (colors, typography, logos) | | onAuthError | (error: unknown) => void | — | Invoked when token acquisition fails (e.g. no signed-in account). Use it to present a login UI. | | tools | ClientTool[] | — | Client-side tools the model can call, executed in the browser (see Client tools). |

Chat

The parent container must use display: flex with a defined height (e.g. height: 100% or height: 100vh) for the chat to fill the available space correctly.

| Prop | Type | Default | Description | | ---------------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ | | chat | ChatDetailResponse | required | Chat data from useChatLoader | | hideConnectors | boolean | false | Hide the connectors dropdown in chat input | | useFullWidth | boolean | false | Remove the default max-width constraints on conversation, input, and error/no-LLM messages so they fill the parent | | run | ChatRun | — | A run the host already owns (see Headless runs). Omit it and Chat owns the run itself |

ChatView

The same UI as Chat, but it never owns a run: pass one from useChatRun. Use it when the host drives the conversation itself.

| Prop | Type | Default | Description | | ---------------- | -------------------- | -------- | ---------------------------------------- | | chat | ChatDetailResponse | required | Chat data from useChatLoader | | run | ChatRun | required | The run to render, from useChatRun | | hideConnectors | boolean | false | Hide the connectors dropdown | | useFullWidth | boolean | false | Remove the default max-width constraints |

BlankChat

| Prop | Type | Default | Description | | ------------------ | ----------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------- | | onSubmit | (input: string, files?: FileUIPart[]) => Promise<boolean> | required | Message submit handler | | initialPrompt | string | — | Pre-fill the input | | hideIllustration | boolean | false | Hide the welcome illustration | | hideConnectors | boolean | false | Hide the connectors dropdown in chat input | | useFullWidth | boolean | false | Remove the default max-width constraints on illustration, input, and no-LLM message so they fill the parent |

Client tools

Pass tools to AxiumAgentProvider to let the model call functions that run in the browser. Each tool's definition (name, description, inputSchema) is sent to the API so the model knows about it; when the model calls the tool, its execute handler runs client-side and the return value is sent back to the model as the tool output. The chat then continues automatically.

import {
  AxiumAgentProvider,
  type ClientTool,
} from '@mobilabsolutions/axium-agent-sdk';

const tools: ClientTool[] = [
  {
    name: 'get_current_location',
    description: "Get the user's current geographic coordinates.",
    // JSON Schema for the tool input.
    inputSchema: { type: 'object', properties: {}, required: [] },
    async execute() {
      const pos = await new Promise<GeolocationPosition>((resolve, reject) =>
        navigator.geolocation.getCurrentPosition(resolve, reject),
      );
      return { lat: pos.coords.latitude, lng: pos.coords.longitude };
    },
  },
];

<AxiumAgentProvider
  baseUrl={baseUrl}
  msalInstance={msal}
  scopes={scopes}
  tools={tools}
>
  {/* … */}
</AxiumAgentProvider>;

ClientTool fields:

| Field | Type | Description | | ------------- | ------------------------------------------- | ------------------------------------------------------------------------------ | | name | string | Unique tool name matching ^[a-zA-Z0-9_-]{1,64}$. | | description | string | What the tool does; shown to the model (1–2048 chars). | | inputSchema | JSONSchema7 | JSON Schema for the tool input (must be serializable). | | execute | (input, ctx) => Output \| Promise<Output> | Runs in the browser; the return value is sent back to the model as the output. |

Notes:

  • Names must be unique and are validated on the client; invalid, duplicate, or reserved names are dropped with a console.error.
  • Reserved names (web_search_preview, visualize, visualize_read_me) and names that collide with a connector tool cannot be overridden.
  • Up to 32 client tools are accepted.
  • Throwing inside execute returns an error output to the model rather than crashing the chat.

Headless runs

useChatRun owns one chat's run: the request to the API, the message stream, client-tool dispatch, and the auto-generated title. It renders nothing, so a host can start a conversation from a button anywhere in its own UI, keep the user on the page they were on, and show progress however it likes.

import {
  useChatRun,
  useCreateChat,
  useChatLoader,
  ChatView,
} from '@mobilabsolutions/axium-agent-sdk';

// 1. Create the chat with its first message. Nothing runs yet.
const { create } = useCreateChat({ done: (id) => setTaskChatId(id) });

// 2. Mount a runner for it. `autoStart` triggers the assistant response.
function BackgroundTask({ chatId }: { chatId: string }) {
  const { chat } = useChatLoader(chatId);
  if (!chat) return null;
  return <Runner chat={chat} />;
}

function Runner({ chat }: { chat: ChatDetailResponse }) {
  const run = useChatRun({
    chat,
    autoStart: true,
    onFinish: () => toast('Task finished'),
  });
  // Nothing rendered: drive a status badge from `run.status` instead.
  return <StatusBadge status={run.status} onCancel={run.stop} />;
}

To let the user watch a run that started this way, hand the same run to the chat UI instead of starting a second one:

const run = useChatRun({ chat, autoStart: true });
return open ? (
  <ChatView chat={chat} run={run} />
) : (
  <StatusBadge status={run.status} />
);

Notes:

  • Keep the hook mounted for the whole run. The model loop continues server-side if the browser goes away, but a client tool call ends the step waiting for a browser-supplied output, so a run that reaches one with no runner mounted stalls. Mount the runner above whatever unmounts (a route, a drawer), not inside it.
  • One run per chat. Two mounts for the same chat.id each hold their own message state and both post to the API. Hoist the hook and share the returned run.
  • autoStart versus useActiveChat. autoStart defaults to the shared isNewChat flag, which suits a single visible chat. Pass it explicitly when more than one run can be in flight, so two runners cannot race for the flag.
  • Streams are not resumable yet: a reload during a run loses the live view. The transcript is persisted server-side when the response completes.

ChatRun

| Field | Type | Description | | ---------- | ----------------------------------------------------------- | -------------------------------------------------------------------- | | messages | UIMessage[] | The live conversation, including the streaming response | | status | 'submitted' \| 'streaming' \| 'ready' \| 'error' | Run state, for progress indicators | | error | Error \| undefined | The last run error | | usage | LanguageModelUsage \| undefined | Token usage of the latest response, when the model reported it | | send | (input: string, files?: FileUIPart[]) => Promise<boolean> | Sends a user message. Resolves false while a response is streaming | | stop | () => void | Aborts the in-flight response |

Hooks

useChatRun

Runs a chat headlessly. Takes { chat, autoStart?, onFinish? } and returns a ChatRun. See Headless runs.

useCreateChat

Creates a new chat on the server. Returns { create, creating }.

When the chat is created, the done callback fires with the new chatId. You should call setIsNewChat(true) (from useActiveChat) before navigating to the chat view — this signals the Chat component to auto-submit the first message to the LLM.

const { setIsNewChat } = useActiveChat();
const { create, creating } = useCreateChat({
  done: (chatId) => {
    setIsNewChat(true);
    navigate(`/chat/${chatId}`);
  },
});

useActiveChat

Returns { isNewChat, setIsNewChat }. The Chat component watches isNewChat — when true and messages are loaded, it automatically triggers the LLM response and resets the flag. This bridges the gap between creating a chat and starting the AI generation.

The flag is shared by every mounted run. If your app can have more than one run in flight, use useChatRun's autoStart option instead of this flag.

useChatLoader

Loads a chat by ID. Returns { chat, isLoading }.

useChats / useDeleteChat

useChats() returns the list of conversations. useDeleteChat() returns a mutation to delete a chat by ID.

License

See LICENSE.md.