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

@agentick/react

v0.9.6

Published

React hooks and components for Agentick applications

Readme

@agentick/react

React bindings for Agentick. Provides hooks around @agentick/client for building chat interfaces and real-time AI applications.

Installation

pnpm add @agentick/react

Quick Start

import { AgentickProvider, useSession, useStreamingText, useContextInfo } from "@agentick/react";

function Chat() {
  const { send } = useSession({ sessionId: "my-session", autoSubscribe: true });
  const { text, isStreaming } = useStreamingText();
  const { contextInfo } = useContextInfo();

  return (
    <div>
      <div className="response">
        {text}
        {isStreaming && <span className="cursor">|</span>}
      </div>

      {contextInfo && (
        <div className="context-bar">
          <span>{contextInfo.modelName}</span>
          <span>{contextInfo.utilization?.toFixed(1)}% context used</span>
        </div>
      )}

      <button onClick={() => send("Hello!")}>Send</button>
    </div>
  );
}

export function App() {
  return (
    <AgentickProvider clientConfig={{ baseUrl: "/api" }}>
      <Chat />
    </AgentickProvider>
  );
}

Hooks

useSession(options?)

Session management hook. Returns methods for sending messages and managing subscriptions.

const {
  sessionId, // Current session ID
  isSubscribed, // Whether subscribed to session events
  subscribe, // Subscribe to session events
  unsubscribe, // Unsubscribe from session events
  send, // Send a message
  abort, // Abort current execution
  close, // Close the session
  accessor, // Direct SessionAccessor for advanced use
} = useSession({
  sessionId: "my-session", // Optional - creates ephemeral session if omitted
  autoSubscribe: true, // Auto-subscribe on mount
});

// Send a simple text message
await send("Hello!");

// Send with full message structure
await send({
  message: {
    role: "user",
    content: [{ type: "text", text: "Hello!" }],
  },
});

useStreamingText(options?)

Subscribe to streaming text from model responses. Automatically accumulates text deltas.

const {
  text, // Accumulated text from current response
  isStreaming, // Whether currently receiving text
  clear, // Clear accumulated text
} = useStreamingText({
  enabled: true, // Enable/disable subscription
});

// Display with typing indicator
<div>
  {text}
  {isStreaming && <span className="cursor">|</span>}
</div>;

useContextInfo(options?)

Subscribe to context utilization information. Updated after each model response with token usage and model capabilities.

const {
  contextInfo, // Latest context info (null before first response)
  clear, // Clear context info
} = useContextInfo({
  sessionId: "my-session", // Optional - filter by session
  enabled: true, // Enable/disable subscription
});

if (contextInfo) {
  console.log(contextInfo.modelId); // "gpt-4o" | "claude-3-5-sonnet" | etc.
  console.log(contextInfo.modelName); // Human-readable name
  console.log(contextInfo.provider); // "openai" | "anthropic" | etc.
  console.log(contextInfo.contextWindow); // Total context window size
  console.log(contextInfo.inputTokens); // Input tokens this tick
  console.log(contextInfo.outputTokens); // Output tokens this tick
  console.log(contextInfo.totalTokens); // Total tokens this tick
  console.log(contextInfo.utilization); // Context utilization % (0-100)
  console.log(contextInfo.maxOutputTokens); // Max output tokens for model
  console.log(contextInfo.supportsVision); // Model supports vision
  console.log(contextInfo.supportsToolUse); // Model supports tools
  console.log(contextInfo.isReasoningModel); // Extended thinking model

  // Cumulative usage across all ticks in execution
  console.log(contextInfo.cumulativeUsage?.inputTokens);
  console.log(contextInfo.cumulativeUsage?.outputTokens);
  console.log(contextInfo.cumulativeUsage?.ticks);
}

ContextInfo Interface

interface ContextInfo {
  modelId: string; // Model identifier (e.g., "gpt-4o")
  modelName?: string; // Human-readable name
  provider?: string; // Provider name
  contextWindow?: number; // Context window size in tokens
  inputTokens: number; // Input tokens this tick
  outputTokens: number; // Output tokens this tick
  totalTokens: number; // Total tokens this tick
  utilization?: number; // Context utilization % (0-100)
  maxOutputTokens?: number; // Max output tokens
  supportsVision?: boolean; // Vision capability
  supportsToolUse?: boolean; // Tool use capability
  isReasoningModel?: boolean; // Extended thinking capability
  tick: number; // Current tick number
  cumulativeUsage?: {
    inputTokens: number;
    outputTokens: number;
    totalTokens: number;
    ticks: number;
  };
}

useEvents(options?)

Subscribe to raw stream events. Use for advanced event handling.

const {
  event, // Latest event
  clear, // Clear current event
} = useEvents({
  sessionId: "my-session", // Optional - filter by session
  filter: ["tool_call", "tool_result"], // Optional - filter by event type
  enabled: true,
});

useEffect(() => {
  if (event?.type === "tool_call") {
    console.log("Tool called:", event.name);
  }
}, [event]);

useConnection()

Connection state for the SSE transport.

const {
  state, // "disconnected" | "connecting" | "connected"
  isConnected, // Convenience boolean
  isConnecting, // Convenience boolean
} = useConnection();

<div className={`status ${isConnected ? "online" : "offline"}`}>
  {isConnected ? "Connected" : "Disconnected"}
</div>;

useConnectionState()

Alias for useConnection(). Returns just the connection state string.

const state = useConnectionState(); // "disconnected" | "connecting" | "connected"

useClient()

Direct access to the underlying AgentickClient for advanced use cases.

const client = useClient();

// Direct client access
const session = client.session("my-session");
const channel = session.channel("custom");
channel.publish("event", { data: "value" });

useLineEditor(options)

React wrapper around @agentick/client's LineEditor class. Provides readline-quality editing with completion support via useSyncExternalStore.

import { useLineEditor } from "@agentick/react";

const { value, cursor, completion, completedRanges, editor } = useLineEditor({
  onSubmit: (text) => send(text),
});

// Register completion sources via the raw editor
useEffect(() => {
  return editor.registerCompletion({
    id: "file",
    match({ value, cursor }) {
      const idx = value.lastIndexOf("#", cursor - 1);
      if (idx < 0) return null;
      return { from: idx, query: value.slice(idx + 1, cursor) };
    },
    resolve: async ({ query }) => searchFiles(query),
  });
}, [editor]);

Returns { value, cursor, completion, completedRanges, handleInput, setValue, clear, editor }. The editor property is the raw LineEditor instance. The completion property is CompletionState | null.

For terminal UIs, use useLineEditor from @agentick/tui which adds Ink keystroke normalization. See COMPLETION.md for the full completion system reference.

Chat Hooks

These hooks wrap the Chat Primitives from @agentick/client. See the client docs for the underlying ChatSession, MessageLog, ToolConfirmations, and MessageSteering classes.

useChat(options?)

Full chat controller hook — messages, steering, tool confirmations, and attachments in one call. Wraps ChatSession with useSyncExternalStore. Auto-subscribes to the SSE transport by default (set autoSubscribe: false to manage subscription separately via useSession).

import { useChat } from "@agentick/react";

function Chat({ sessionId }: { sessionId: string }) {
  const {
    messages, // ChatMessage[]
    chatMode, // "idle" | "streaming" | "confirming_tool"
    toolConfirmation, // { request, respond } | null
    lastSubmitted, // Optimistic user message text
    queued, // Queued messages
    isExecuting, // Execution in progress
    mode, // "steer" | "queue"
    attachments, // Attachment[] (pending, not yet sent)

    submit, // Send or queue based on mode (drains attachments)
    steer, // Always send immediately (drains attachments)
    queue, // Always queue (no attachments)
    interrupt, // Abort + send (drains attachments)
    flush, // Flush next queued
    respondToConfirmation,
    clearMessages,
    setMode,
    removeQueued,
    clearQueued,
    addAttachment, // (input: AttachmentInput) => Attachment
    removeAttachment, // (id: string) => void
    clearAttachments, // () => void
  } = useChat({ sessionId, mode: "queue" });

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id} className={msg.role}>
          {typeof msg.content === "string" ? msg.content : "..."}
        </div>
      ))}

      {toolConfirmation && (
        <dialog open>
          <p>Allow {toolConfirmation.request.name}?</p>
          <button onClick={() => respondToConfirmation({ approved: true })}>Allow</button>
          <button onClick={() => respondToConfirmation({ approved: false })}>Deny</button>
        </dialog>
      )}

      <input
        onKeyDown={(e) => {
          if (e.key === "Enter") submit(e.currentTarget.value);
        }}
      />
    </div>
  );
}

Options are captured at mount time. Changing them requires a new sessionId.

Custom Chat Modes

type MyMode = "idle" | "working" | "needs_approval";

const { chatMode } = useChat<MyMode>({
  sessionId,
  deriveMode: ({ isExecuting, hasPendingConfirmation }) => {
    if (hasPendingConfirmation) return "needs_approval";
    if (isExecuting) return "working";
    return "idle";
  },
});
// chatMode is typed as MyMode

useMessages(options?)

Message accumulation only. Use when you don't need steering or confirmations.

import { useMessages } from "@agentick/react";

const { messages, clear } = useMessages({
  sessionId: "my-session",
  transform: customTransform, // Optional custom MessageTransform
  initialMessages: [], // Pre-loaded history
});

useToolConfirmations(options?)

Tool confirmation management only. Use for custom confirmation UIs.

import { useToolConfirmations } from "@agentick/react";

const { pending, respond } = useToolConfirmations({
  sessionId: "my-session",
  policy: (req) => (req.name === "read_file" ? { action: "approve" } : { action: "prompt" }),
});

if (pending) {
  // Show confirmation UI
  respond({ approved: true });
}

useMessageSteering(options?)

Input-side message routing with queue/steer modes.

import { useMessageSteering } from "@agentick/react";

const { queued, isExecuting, mode, submit, steer, queue, interrupt, flush, setMode } =
  useMessageSteering({
    sessionId: "my-session",
    mode: "queue",
    flushMode: "sequential",
  });

Progressive Disclosure

| Level | Hook | Use case | | ----- | ------------------------------------------------------------- | ------------------------------------ | | 0 | useChat | Full chat — one hook does everything | | 1 | useChat + options | Custom modes, policies, transforms | | 2 | useMessages + useToolConfirmations + useMessageSteering | Compose individual primitives | | 3 | useEvents + custom reducer | Full control, build your own |

Provider

AgentickProvider

Wraps your app to provide the Agentick client context.

<AgentickProvider
  clientConfig={{
    baseUrl: "https://api.example.com", // Required - API base URL
    token: "auth-token", // Optional - auth token
  }}
>
  <App />
</AgentickProvider>

Types

All hooks are fully typed. Import types from the package:

import type {
  // Provider types
  AgentickProviderProps,
  AgentickContextValue,
  TransportConfig,

  // Hook types
  UseConnectionOptions,
  UseConnectionResult,
  UseSessionOptions,
  UseSessionResult,
  UseEventsOptions,
  UseEventsResult,
  UseStreamingTextOptions,
  UseStreamingTextResult,
  UseContextInfoOptions,
  UseContextInfoResult,
  ContextInfo,

  // Chat hook types
  UseChatOptions,
  UseChatResult,
  UseMessagesOptions,
  UseMessagesResult,
  UseToolConfirmationsOptions,
  UseToolConfirmationsResult,
  UseMessageSteeringOptions,
  UseMessageSteeringResult,
  ChatMode,
  ChatMessage,
  ToolConfirmationState,
  SteeringMode,
  FlushMode,

  // Re-exported from @agentick/client
  AgentickClient,
  ConnectionState,
  StreamEvent,
  SessionAccessor,
  SendInput,
  ClientExecutionHandle,
  SessionStreamEvent,
  ClientTransport,
} from "@agentick/react";

// Transform functions (re-exported from @agentick/client)
import {
  timelineToMessages,
  extractToolCalls,
  defaultTransform,
  defaultDeriveMode,
} from "@agentick/react";

License

MIT