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

@hanfani/server

v0.4.0

Published

Runtime services for the Hanfani agent framework: the activity-summary grammar (emit + decode), an audit-log store and activity bus, a policy-monitor (objectives) engine, usage reporting, connection profiles, and an opt-in gate auto-approver.

Readme

@hanfani/server

build status npm version npm downloads bundle JSDocs License

Runtime services for the Hanfani agent framework — the layer above the headless @hanfani/core engine.

The agent proposes, an approver decides, the server acts. The approver is a human — or, when the user enables it in settings, a deterministic rule the user configured. The model never approves unless you authorize to do so.

Where @hanfani/core defines what a workflow and an agent are (pure, no I/O), @hanfani/server provides the runtime that observes and serves them: the audit grammar the server emits, an audit-log store and activity bus, a policy-monitor (objectives) engine, usage reporting, connection profiles, and a settings-driven auto-approval mechanism. Every module is generic — domain specifics are injected — so any workflow can use them.

Determinism

The framework's aim is that the same inputs always produce the same outcome: same objective config + same ordered activity ⇒ same result, every time. Completion is decided by server facts (post-approval effect events), never by model prose. The one naturally non-deterministic step is human approval — so auto-approval (below) exists to make that branch deterministic too, by the user's choice, without ever letting the model approve its own actions.

Install

pnpm add @hanfani/server @hanfani/core

hono is an optional peer dependency, needed only if you use the HTTP route registrars (registerAuditRoutes, registerUsageRoute, registerObjectivesRoutes, registerConnectionProfileRoute).

What's inside

The activity grammar — owned here

The audit stream carries only a kind and a free-text summary. The exact shape of that summary is a contract between whoever writes an audit entry (the server's action ledger) and whoever reads it (objective monitors, the UI, an auto-approver). Because the server emits it, the server owns it — and the emitter and decoder live in the same module so they cannot silently drift:

import { formatActivitySummary, parseActivityEvent } from '@hanfani/server'

const summary = formatActivitySummary({ kind: 'resolved', decision: 'approved', tool: 'saveDraft' })
// → "approved saveDraft"

parseActivityEvent({ kind: 'resolved', summary }, { tools: ['saveDraft'] })
// → { kind: 'resolved', tool: 'saveDraft', decision: 'approved', structured: true }

The grammar:

| kind | Summary format | Example | | ---------- | --------------------------------- | -------------------- | | gate | the bare tool name | saveDraft | | resolved | approved <tool> | rejected | approved saveDraft | | effect | executed <tool> | executed saveDraft | | finished | finished | finished |

parseActivityEvent is generic — tool is a plain string; pass { tools } to narrow/validate it. A parse(format(x)) round-trip test locks the pair together.

Audit-log store + activity bus

import { createAuditStore, registerAuditRoutes, startAuditBridge } from '@hanfani/server'

const audit = createAuditStore(myStorePort, { actorOf })   // dedup + subscriber bus
registerAuditRoutes(app, audit)                            // GET /api/audit-log (+ /stream)
startAuditBridge({ baseUrl, onEntry: audit.persist })      // consume a pipeline SSE stream

The store logic (dedup window, the in-process bus, the human-actor hook) is framework-owned; you bind the three-method AuditStorePort to your database.

Objectives — a policy-monitor engine

A deterministic monitor over a workflow's activity: given a trigger and a success rule, did the server observe the right facts? Completion is decided by server facts (post-approval effect events), never by model prose. Every domain decision is delegated to an ObjectivePolicy you implement; the engine drives the run lifecycle (create → progress → gate → resolved → success/fail → deadline).

import { createObjectivesEngine, createInMemoryObjectivesStore } from '@hanfani/server/objectives'

const engine = createObjectivesEngine(myEmailPolicy, {
  store: createInMemoryObjectivesStore(), // or your DB-backed ObjectivesStore
  loadPayload,
  emitActivity: audit.persist,
})
audit.subscribe((entry) => void engine.evaluateActivityEntry(entry))

Auto-approval — the determinism mechanism

Auto-approval turns the human-approval branch into a deterministic one by the user's intent. The user enables it in settings and configures declarative rules; a matching gate is then resolved by a pure rule instead of a human click. The decision is a pure function — same settings + same context ⇒ same decision — so approvals replay reproducibly. The model is never in this path; approval authority is the human or the human's pre-declared settings.

import { matchAutoApproval, startGateAutoApprover } from '@hanfani/server'

const settings = {
  enabled: true, // the master switch, toggled from user settings
  rules: [{ id: 'trusted-sender', agent: 'reply', tool: 'saveDraft', when: (c) => trusted.has(c.extra.senderEmail) }],
}

// Pure decision (testable, replayable):
matchAutoApproval(settings, ctx) // → { approved: true, ruleId: 'trusted-sender' }

// Runtime bridge — reads settings live, resolves matching gates, records the deciding rule:
startGateAutoApprover({ subscribe: audit.subscribe, baseUrl, settings: () => settings, resolveContext, onAutoApproved })

Usage & connection profiles

  • getUsageSummary / registerUsageRoute — month-to-date Anthropic spend vs. a budget.
  • getConnectionProfile — the never-connected vs. session-expired vs. store-unavailable state machine, with an injected identity enrich hook.

Dependencies

Depends on zod (schemas) at runtime. @hanfani/core and hono (the HTTP route registrars) are optional peers — pull them in only for the features that use them.

Docs

An auto-generated API reference is available at jsdocs.io. Full guides are coming soon in the Hanfani framework docs.

License

MIT License © Fruitizz