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

@lumifai/harness-react

v0.1.0

Published

Headless React bindings for the Lumif harness browser client: provider, hooks, SSE client, and optional client-side session persistence. The shared browser client and state reducer live in `@lumifai/harness-client`.

Readme

@lumifai/harness-react

Headless React bindings for the Lumif harness browser client: provider, hooks, SSE client, and optional client-side session persistence. The shared browser client and state reducer live in @lumifai/harness-client.

Usage

import {
  HarnessProvider,
  createLocalStorageHarnessSessionStore,
  useHarnessSessionStore,
  useHarnessActions,
  useHarnessState,
} from '@lumifai/harness-react';

export function App() {
  const { bindings, resetSession, sessionKey } = useHarnessSessionStore({
    store: createLocalStorageHarnessSessionStore({
      storageKey: 'my-harness.session',
    }),
  });

  return (
    <>
      <button onClick={resetSession}>New conversation</button>
      <HarnessProvider
        key={sessionKey}
        baseUrl=""
        prefix="/harness"
        autoConnect
        clientOptions={{
          credentials: 'include',
          headers: async () => ({ Authorization: 'Bearer <token>' }),
        }}
        {...bindings}
      >
        <Chat />
      </HarnessProvider>
    </>
  );
}

function Chat() {
  const { messages, displayState } = useHarnessState();
  const { sendMessage } = useHarnessActions();
  // render your own UI
}

For a Mantine-based studio UI, use @lumifai/harness-react-mantine.

clientOptions passes auth and transport settings through to the browser client. Use it for bearer headers, cookies, custom fetch, or a custom EventSource factory.

The provider treats the server snapshot as authoritative after an SSE error. It refreshes the snapshot when the stream fails and reports stream connectivity through connected; the latest run failure is available as displayState.lastError.

Human-in-the-loop

displayState surfaces pending approvals, questions, plan reviews, and tool suspensions. Pending suspensions can be concurrent; use each entry's toolCallId, pendingActionId, and runId to target the correct resume action after reconnect.

Helpers from @lumifai/harness-client (re-exported here) parse suspend payloads:

import {
  defineSuspensionRenderer,
  HarnessProvider,
  useHarnessActions,
  useHarnessState,
} from '@lumifai/harness-react';

const confirmPurchaseRenderer = defineSuspensionRenderer({
  toolName: 'confirm_purchase',
  parse: (suspension) => {
    const payload = suspension.suspendPayload;
    if (!payload || typeof payload !== 'object') return null;
    const { item } = payload as { item?: unknown };
    return typeof item === 'string' ? { item } : null;
  },
  formatDecision: ({ toolCall, toolResult }) => {
    const item =
      toolCall.args &&
      typeof toolCall.args === 'object' &&
      'item' in toolCall.args &&
      typeof (toolCall.args as { item?: unknown }).item === 'string'
        ? (toolCall.args as { item: string }).item
        : 'item';
    const confirmed =
      toolResult.result &&
      typeof toolResult.result === 'object' &&
      'confirmed' in toolResult.result
        ? Boolean((toolResult.result as { confirmed: unknown }).confirmed)
        : false;
    return {
      label: 'Purchase',
      summary: `${item} → ${confirmed ? 'confirmed' : 'cancelled'}`,
    };
  },
  render: ({ payload, resume, busy }) => (
    <button disabled={busy} onClick={() => void resume({ confirmed: true })}>
      Confirm {payload.item}
    </button>
  ),
});

<HarnessProvider suspensionRenderers={[confirmPurchaseRenderer]} ...>
  ...
</HarnessProvider>

resumeToolSuspension accepts the tool's resume payload — for example { answer: 'yes' } for ask_user, { approved: true } for request_access, or { action: 'approved' } for submit_plan. Always pass toolCallId when more than one suspension is pending.

Studio’s conversation transcript renders completed HITL tools as collapsed decision chips (question → answer). Expand a chip to see full detail. Chips are built from message tool_call/tool_result pairs via optional formatDecision on each renderer.

Tool progress cards

Long-running tools can stream progress via Mastra writer.custom with type: 'data-mastracode-tool-progress' (or sandbox stdout/stderr). That updates displayState.activeTools[toolCallId].partialResult / shellOutput.

Opt into progress cards with enableToolProgressCards and optional per-tool renderers (default card when a tool has emitted progress but no custom renderer matches):

import {
  defineToolProgressRenderer,
  HarnessProvider,
} from '@lumifai/harness-react';

const longJobProgress = defineToolProgressRenderer({
  toolName: 'long_job',
  render: ({ tool }) => <pre>{tool.partialResult}</pre>,
});

<HarnessProvider
  enableToolProgressCards
  toolProgressRenderers={[longJobProgress]}
  ...
>
  ...
</HarnessProvider>

HarnessStudio accepts the same props (enableToolProgressCards is OR'd with the provider flag). Cards appear only when the tool has partialResult or shellOutput.

Parallel calls of the same tool are separate entries keyed by toolCallId in activeTools. Progress updates only touch that call’s slot. Custom renderers receive { toolCallId, tool } (including tool.args) so you can label each instance; the default card shows toolCallId plus a short args summary.

Progress is turn-scoped in live activeTools. The server also persists progress-bearing activeTools so a reconnect / cold session load can rehydrate the last snapshot for the UI — it does not resume the tool process itself.

Custom session store

Implement HarnessSessionStoreAdapter for Zustand, Redux, IndexedDB, etc.:

import type { HarnessSessionStoreAdapter } from '@lumifai/harness-react';

const zustandStore: HarnessSessionStoreAdapter = {
  load: () => useMyStore.getState().harnessSession,
  save: (session) => useMyStore.getState().setHarnessSession(session),
  clear: () => useMyStore.getState().clearHarnessSession(),
};

useHarnessSessionStore({ store: zustandStore });