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

@dpid/prefrontal-ai

v0.3.0

Published

LLM-driven deliberation layer for needs-based AI agents

Readme

@dpid/prefrontal-ai

LLM-driven deliberation layer on top of @dpid/needs-based-ai. Needs remain the ground truth for agent behavior; the LLM modulates, inhibits, and expresses on top of that substrate rather than replacing it.

Core invariants: the tick is never blocked (LLM calls are async, results apply on a later tick), the LLM is optional (with none configured, or on timeout/failure, the agent behaves exactly like a vanilla needs-based agent), and expression never drives behavior (monologue is generated from decisions already made).

Install

npm install @dpid/prefrontal-ai @dpid/needs-based-ai @dpid/command-state-machine
# Optional: only if you use AnthropicLlmClient
npm install @anthropic-ai/sdk

@anthropic-ai/sdk is an optional peer dependency. The package is fully importable without it; it is resolved lazily on the first AnthropicLlmClient call.

Quick start

PrefrontalAgent wraps a needs-based Agent without subclassing it. Its rankingFunction is the sole behavioral hook into needs-based-ai; drive its clock with update(dt).

import { AdvertisementHandler } from '@dpid/needs-based-ai';
import { PrefrontalAgent, MockLlmClient, candidateIdFor } from '@dpid/prefrontal-ai';

const prefrontal = PrefrontalAgent.create(agent, {
  llmClient: MockLlmClient.create(), // swap for Cached/Anthropic; omit for vanilla behavior
});

// Hand the ranking function to needs-based-ai. It is pure and synchronous — all LLM work
// happens outside the ranking call, biasing later ticks.
const handler = AdvertisementHandler.create(
  agent,
  'targetFound',            // transition fired when a target is reached
  0,                        // distance weight
  prefrontal.rankingFunction
);

// Each frame: advance the prefrontal clock (ticks willpower, suppression timers, the gate,
// and consumes any settled deliberation). Sits alongside your other commands.
function tick(dt: number): void {
  prefrontal.update(dt);
}

// Optional feedback + drama hooks. candidateIdFor(ad) is the canonical candidate id
// (the same string used in deliberation contexts) — never hand-build it.
prefrontal.reportOutcome('food', candidateIdFor(ad), true); // feeds the plan-failure trigger
prefrontal.onMonologue.addListener((e) => render(e.text));
prefrontal.onOverrideFailed.addListener((e) => animateGivingIn(e.needId));

// Read-only inspector state for HUDs. Unlike buildContext(), polling these never
// perturbs the trend fields the next deliberation context reports.
renderBars(prefrontal.willpower, prefrontal.activeSuppressions);

Events emitted: onDeliberationStarted / onDeliberationCompleted, onSuppressionStarted / onSuppressionEnded, onOverrideFailed, onMonologue. UX is the game's job; the library only emits.

LlmClient options

All clients implement the same LlmClient interface (deliberate + express) and are interchangeable in config.llmClient.

| Client | Factory | Use | | --- | --- | --- | | NullLlmClient | NullLlmClient.create() | Inert. Agent behaves exactly like vanilla needs-based-ai. The default when no client is configured. | | MockLlmClient | MockLlmClient.create(results?, options?) | FIFO queue of scripted DeliberationResults + records received contexts. For tests and offline sims. | | CachedLlmClient | CachedLlmClient.create(inner, quantizer?, options?) | Decorator over any client. Memoizes deliberation decisions by a quantized context, coalesces concurrent identical calls into one inner call, and caps concurrency. Never caches express(). | | AnthropicLlmClient | AnthropicLlmClient.create(config) | Live constrained deliberation over @anthropic-ai/sdk via forced tool use. budget.tier picks the model (fast → Haiku-class, escalated → Sonnet-class). | | WebLlmClient | WebLlmClient.create(config?) | Local, in-browser deliberation on the player's GPU over @mlc-ai/web-llm (WebGPU). No API key, no per-token cost, no network after the one-time model download. See below. |

CachedLlmClient is the many-agent scaling win: wrap one inner client and share the single decorator across every agent so identical situations reuse one decision. Its stats getter exposes { hits, misses, coalesced, evictions }. Options: ttlTicks (TTL against ctx.tick), maxEntries (LRU bound), maxConcurrent (inner-call ceiling with a FIFO overflow queue).

import { CachedLlmClient, AnthropicLlmClient } from '@dpid/prefrontal-ai';

const shared = CachedLlmClient.create(AnthropicLlmClient.create({ apiKey: process.env.ANTHROPIC_API_KEY }));
// pass `shared` as config.llmClient for every agent, then read shared.stats

Browser usage caveat

AnthropicLlmClient.create({ dangerouslyAllowBrowser: true }) maps to the SDK's browser-access flag. Direct browser calls expose your API key to anyone who can read the page — acceptable for local dev only. For anything shared, front the API with a thin server-side proxy and point baseURL at it, keeping the key server-side.

WebLLM (local, in-browser)

WebLlmClient runs deliberation on the player's own GPU via @mlc-ai/web-llm (WebGPU): no API key, no per-token cost, and no network traffic after the one-time model download. @mlc-ai/web-llm is an optional peer dependency — the package is fully importable without it, and it is resolved lazily on the first live call.

import { CachedLlmClient, WebLlmClient } from '@dpid/prefrontal-ai';

// No WebGPU (Firefox/Safari today, older Chrome) → skip the download; agents run vanilla.
if (!WebLlmClient.isSupported()) return runVanilla();

const web = WebLlmClient.create({
  model: 'Llama-3.2-3B-Instruct-q4f16_1-MLC', // the default
  initProgressCallback: (r) => renderProgress(r.progress, r.text), // 0..1 + status text
});

// The model is a multi-hundred-MB–GB one-time download — gate this behind a user action
// (a "Load model" button), not page load. Resolves once ready, rejects if init fails.
await web.preload();

// One on-GPU engine generates serially, so cap the shared cache at a single inner call.
const llmClient = CachedLlmClient.create(web, undefined, { maxConcurrent: 1 });
// Pass `llmClient` as config.llmClient for every agent.

deliberate()/express() never await the (minutes-long) init: until state === 'ready' they reject immediately and the agent behaves exactly like vanilla needs-based-ai (the Deliberator's cooldown keeps that from thrashing). Drive a loading screen with preload() + initProgressCallback; deliberations begin flowing once it resolves.

Models (ids from prebuiltAppConfig.model_list; sizes are approximate WebGPU VRAM / download):

| Model id | Size | Notes | | --- | --- | --- | | Llama-3.2-1B-Instruct-q4f16_1-MLC | ~0.9 GB | Low-VRAM; weakest schema adherence. | | Llama-3.2-3B-Instruct-q4f16_1-MLC | ~2.2 GB | Default. Good balance of size and decision quality. | | Llama-3.1-8B-Instruct-q4f16_1-MLC | ~5.0 GB | Best decisions; needs a capable GPU. |

Run inference in a Worker. Main-thread inference janks the game loop. Create a CreateWebWorkerMLCEngine and hand it in via the engine escape hatch (an injected engine starts ready, so preload() becomes a no-op):

import { CreateWebWorkerMLCEngine } from '@mlc-ai/web-llm';

const engine = await CreateWebWorkerMLCEngine(
  new Worker(new URL('./mlc.worker.ts', import.meta.url), { type: 'module' }),
  'Llama-3.2-3B-Instruct-q4f16_1-MLC',
  { initProgressCallback }
);
const web = WebLlmClient.create({ engine }); // inference stays off the main thread

Budget. Local 3B inference is seconds, not milliseconds — set budget.timeoutMs in the 3000–8000ms range (vs the API-client default). An optional escalatedModel spins up a second engine for the escalated tier; leave it unset (the default) to serve both tiers from one engine and avoid doubling VRAM + download.

Browser support. Chrome and Edge ship WebGPU. Safari and Firefox coverage is partial/behind flags; isSupported() + a vanilla fallback is the mitigation. See npm run example:webllm for a full page (progress bar, ?mock offline fallback, unsupported-browser banner).

Examples

Two runnable variants of the same 20-agent simulation. Both run offline with a scripted MockLlmClient by default; set ANTHROPIC_API_KEY to swap in the live AnthropicLlmClient (fast tier) through the identical code path.

npm run example:hidden      # behavior only; prints per-tick decisions + final cache hit-rate
npm run example:monologue   # same sim; renders the inner voice + suppression/override drama
npm run example:webllm      # browser page: same sim over WebGPU; add ?mock to run offline

The WebLLM page serves at http://localhost:5173. Load /?mock to watch scripted deliberations with zero download (fully offline), or / to download the model and deliberate on your GPU (Chrome/Edge; a "Load model" button gates the ~2GB fetch).

Scripts

npm run build       # Build with tsup (ESM + CJS + d.ts)
npm run typecheck   # Type check library and examples with tsc
npm test            # Run tests with vitest
npm run test:watch  # Run tests in watch mode