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

hai-agents

v1.0.0

Published

TypeScript SDK for H Company's Computer-Use Agents: autonomous agents powered by Holo.

Readme

Installation

npm install hai-agents

Node.js 18 or newer is required. Get an API key at portal.hcompany.ai and export it:

export HAI_API_KEY=hk-...

Quickstart

Launch the built-in h/web-surfer-holo3-1-35b agent, which ships with its own browser, and describe the task in plain language. runSession polls until the agent finishes and returns the final answer.

import { HaiAgentsClient } from "hai-agents";

const client = new HaiAgentsClient(); // reads HAI_API_KEY from the environment

const result = await client.runSession({
  agent: "h/web-surfer-holo3-1-35b",
  messages: "What are the top 3 stories on Hacker News right now?",
});

console.log(result.status); // "completed"
console.log(result.answer);

result is a SessionRunResult: id, status, answer, the accumulated events, and finalChanges.

How a session works

A session is one run of an agent against a task. It moves through a small set of states: pending, running, and then a settled state such as completed, idle, failed, timed_out, or interrupted.

You drive a session two ways. runSession creates it and resolves once it settles, which suits one-shot tasks. startSession creates it and returns a handle right away, so you can read and steer the agent while it works.

const session = await client.startSession({
  agent: "h/web-surfer-holo3-1-35b",
  messages: "Find the top story on Hacker News",
});

console.log(session.id);
const result = await session.waitForCompletion();
console.log(result.status, result.answer);

Watch and steer a running session

A handle bound to the session id exposes the full lifecycle. Read the agent's progress at three levels of detail:

await session.status();                  // cheap snapshot: state, step count, token usage
await session.changes({ fromIndex: 0 }); // new events and the final answer, long-polled
await session.get();                     // the full Session resource

While the session is not in a terminal state, you can intervene:

await session.sendMessage({ type: "user_message", message: "Only consider the last 24 hours" });
await session.pause();       // halt with state preserved
await session.resume();      // continue where it left off
await session.forceAnswer(); // stop exploring and answer from what it has
await session.cancel();      // stop for good; ends in "interrupted"

sendMessage redirects the agent on its next step. Sending a message to an idle session also wakes it.

Multi-turn sessions

By default a session ends as soon as the agent answers. Set idleTimeoutS to keep it open: after each answer the session goes idle and waits that long for your next message, carrying its full context and browser state across turns.

const session = await client.startSession({
  agent: "h/web-surfer-holo3-1-35b",
  idleTimeoutS: 600,
  messages: "Find the top story on Hacker News",
});
const first = await session.waitForCompletion();

await session.sendMessage({ type: "user_message", message: "Now summarize its comments" });
const second = await session.waitForCompletion();

Structured output

Pass a Zod v4 schema as answerSchema and the agent's final answer resolves as a parsed, typed value. The schema is sent as the agent's answer format; the raw wire value stays at result.finalChanges.answer. Zod is an optional peer dependency, only needed when you use this.

import { HaiAgentsClient } from "hai-agents";
import { z } from "zod";

const Jobs = z.object({
  jobs: z.array(z.object({ title: z.string(), company: z.string() })),
});

const client = new HaiAgentsClient();
const result = await client.runSession({
  agent: "h/web-surfer-holo3-1-35b",
  messages: "Find 3 open ML engineering roles in Paris.",
  answerSchema: Jobs,
});

for (const job of result.answer?.jobs ?? []) {
  console.log(job.title, "@", job.company); // typed via z.infer
}

A completed answer that does not match the schema throws AnswerValidationError, with the raw payload on .raw. Sessions that end without completing resolve with their raw answer untouched.

Custom tools

Give the agent tools that run in your own process. Declare each tool with a JSON schema and a function; the SDK registers them on the session, runs them when the agent calls them, and posts the results back so the agent can continue.

import { HaiAgentsClient, tool } from "hai-agents";

const getWeather = tool({
  name: "get_weather",
  description: "Get the current weather for a city.",
  inputSchema: {
    type: "object",
    properties: { city: { type: "string" } },
    required: ["city"],
  },
  fn: async ({ city }) => `Sunny in ${city}, 24C`,
});

const client = new HaiAgentsClient();

const result = await client.runSession({
  agent: "h/web-surfer-holo3-1-35b",
  messages: "What should I wear in Paris today?",
  tools: [getWeather],
});

console.log(result.answer);

Tool functions may be sync or async. A tool that throws is reported to the agent as a tool error rather than crashing the run.

Browser profiles and vaults

Start a session on a browser that already knows the user. A browser profile restores saved cookies and storage from an earlier session, and a vault lets the agent sign in to sites with secrets that never enter its context. Bind both through per-run overrides:

const result = await client.runSession({
  agent: "h/web-surfer-holo3-1-35b",
  messages: "Open my dashboard and report any new alerts",
  overrides: {
    "agent.environments[kind=web].browser_profile_id": "<profile-id>",
    "agent.environments[kind=web].vault_id": "<vault-id>",
  },
});

Inspect and share sessions

List past sessions and create a public replay link:

const page = await client.sessions.listSessions({ size: 10 });
for (const summary of page.items) {
  console.log(summary.id, summary.status);
}

const link = await client.sessions.shareSession({ id: "<session-id>" });
console.log(link.shareUrl);

Regions and configuration

The client targets the EU region by default. Point it at the US region or a custom URL, and pass the API key in code when you do not want to use the environment variable:

import { HaiAgentsClient, HaiAgentsEnvironment } from "hai-agents";

const client = new HaiAgentsClient({ environment: HaiAgentsEnvironment.Us });
// or
const client = new HaiAgentsClient({ baseUrl: "https://agp.hcompany.ai", apiKey: "hk-..." });

Errors

import { HaiAgentsError, HaiAgentsTimeoutError, AnswerValidationError } from "hai-agents";

HaiAgentsError is the base for HTTP failures and carries .statusCode and .body. HaiAgentsTimeoutError is thrown when a request exceeds its time budget. HaiAgents.UnprocessableEntityError is the 422 raised when a request fails validation. AnswerValidationError is thrown when a completed answer does not match answerSchema, with the unparsed value on .raw.

Webhooks

Verify the signature on an incoming webhook before trusting it:

import { verifyWebhook, WebhookVerificationError } from "hai-agents";

const event = verifyWebhook(rawBody, signature, timestamp, secret);
console.log(event.type, event.data);

Documentation

Guides, core concepts, and the full API reference live at hub.hcompany.ai/agent-api.

License

MIT