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

@braedonsaunders/appkit-ai

v2.2.0

Published

App-agnostic bounded agent loop and streaming assistant interface, with provider policy supplied by the host app.

Readme

@braedonsaunders/appkit-ai

The shared multi-step agent layer for bounded, tenant-aware assistant surfaces.

The server entry point accepts an AI SDK LanguageModel, system prompt, prior model messages, and a tenant-bound ToolSet. It deliberately imports no app schema, tenant package, provider credential store, or domain prompt. A consuming app resolves those at the request boundary and exposes only tools whose execute functions already enforce its RequestContext and RBAC policy.

@braedonsaunders/appkit-ai/react supplies the streaming assistant thread, UI-message decoder, markdown renderer, generic tool-use cards, abort control, composer, welcome and disabled states. Conversation persistence and the HTTP transport stay app-owned. Hosts can place contextual controls in the panel's fixed header with the optional headerActions prop and replace the stock first-message card with a full-height application stage through emptyContent. assistantAvatar places an application-owned employee identity in the existing 28px assistant-message slot, with the stock Sparkles marker retained as the fallback. Consecutive tool calls collapse into one quiet activity line showing the newest action and total step count; operators can expand it to inspect every input and result without letting a long run overwhelm the thread. Long links, identifiers, code blocks, and markdown tables wrap within the conversation width instead of creating a second horizontal scroll area.

For long histories, pass only the newest page in initialMessages together with hasOlderMessages and onLoadOlderMessages. Reaching the top requests the next page automatically, while the button remains available for keyboard and assistive-technology users. After the host prepends that page, the panel keeps the message being read at the same visual position.

The composer remains extensible without being replaced. composerActions adds controls beside the text input, composerContent renders application-owned draft state above it, and composerDraft supplies a file-only fallback prompt plus parts for the optimistic user turn. Persisted user messages can include { type: 'file', filename, url? } parts, which render as compact attachment links. Its one-line textarea shares the icon buttons' 40px geometry and explicit line height, keeping typed and placeholder text vertically centered.

While the first assistant parts are streaming, AgentPanel renders a subtle three-dot typing cadence through the exported AgentTypingIndicator. Its pure CSS motion uses AppKit duration/easing tokens, is independent of an app's Tailwind content scan, and becomes a static ellipsis under reduced motion.

Assistant messages may also contain a typed secret-request part with a request id, provider label, credential label, purpose, optional HTTPS help URL, and pending, stored, or expired status. AgentPanel renders the exported AgentSecretRequestCard inline and delegates submission and cancellation to onSubmitSecretRequest / onCancelSecretRequest. The credential is held only in the uncontrolled password field, cleared before the submit handler runs, and never added to message parts, React state, rendered text, or AppKit-owned persistence. The application remains responsible for authorization, sealing, storage, audit, and updating the durable request status.

Governed actions can use a typed approval-request part with an approval id, category, human-readable description, optional exact-action details, and a durable pending/approved/rejected/expired status. AgentPanel renders the exported AgentApprovalRequestCard inline, so an agent can end a suspended turn with a clear handoff instead of a bare tool call. Applications persist and authorize the decision through onDecideApprovalRequest; AppKit never treats rendering the card as authorization.

<AgentPanel
  enabled
  initialMessages={[{
    id: 'assistant-credential',
    role: 'assistant',
    parts: [{
      type: 'secret-request',
      requestId: request.id,
      providerLabel: 'Delivery provider',
      credentialLabel: 'API key',
      purpose: 'Authorize approved carrier status updates.',
      helpUrl: 'https://provider.example/docs/api-keys',
      status: request.status,
    }],
  }]}
  onSubmitSecretRequest={async (requestId, secret) => {
    await storeSealedCredential({ requestId, secret })
  }}
  onCancelSecretRequest={expireCredentialRequest}
/>

Applications with durable session dispatch can pass dispatchState and an ordered queuedMessages collection. While work is running, recovering, or already queued, new composer submissions go through the app-owned enqueue callback instead of bypassing the queue. AppKit presents queue position and lifecycle state and exposes optional edit, remove, and retry actions; the host continues to own persistence, authorization, FIFO claiming, idempotency, and recovery.

<AgentPanel
  enabled
  dispatchState="running"
  queuedMessages={queue.map((entry) => ({
    id: entry.id,
    text: entry.prompt,
    position: entry.position,
    status: entry.status,
    editable: entry.status === 'queued',
    removable: entry.status === 'queued',
    retryable: entry.status === 'failed',
  }))}
  enqueue={persistQueuedTurn}
  onEditQueuedMessage={openQueueEditor}
  onRemoveQueuedMessage={removeQueuedTurn}
  onRetryQueuedMessage={retryQueuedTurn}
  send={startTurn}
/>
import { runAgentTurn } from '@braedonsaunders/appkit-ai'

return runAgentTurn({
  model,
  system,
  messages,
  tools: tenantBoundTools,
  onComplete: persistAssistantTurn,
})

For visual tool loops, call pruneVisualToolContext from the AI SDK prepareStep hook. It removes historical and exact-duplicate tool-result images while leaving the newest distinct frames, tool structure, text, and user-supplied images intact. Persist the complete screenshot record separately.