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

@priest-ai/core

v2.3.0

Published

TypeScript/Node.js SDK for the priest AI orchestration protocol

Downloads

172

Readme

@priest-ai/core

TypeScript SDK for the priest AI orchestration protocol.

Node.js 18+ · TypeScript 5+ · One dependency (better-sqlite3 for SQLite sessions)


Overview

@priest-ai/core is a TypeScript package that implements the priest protocol spec v2.3.0 natively — no Python server, no FFI. It is designed for Node.js backends, serverless functions, CLI tools, and any TypeScript host that needs to talk to a local or remote AI provider.

The core API is two methods on PriestEngine:

| Method | Returns | Use when | |--------|---------|----------| | run(request) | Promise<PriestResponse> | You need structured metadata (usage, latency, session info) | | stream(request) | AsyncGenerator<string> | You want to yield text as it arrives |


Installation

npm install @priest-ai/core
# or
pnpm add @priest-ai/core

Then import:

import { PriestEngine, OllamaProvider, FilesystemProfileLoader } from '@priest-ai/core';

Quick Start

Single run with Ollama

import { PriestEngine, OllamaProvider, FilesystemProfileLoader } from '@priest-ai/core';

const engine = new PriestEngine(
  new FilesystemProfileLoader('./profiles'),
  undefined,
  { ollama: new OllamaProvider('http://localhost:11434') },
);

const response = await engine.run({
  config: { provider: 'ollama', model: 'llama3.2' },
  prompt: 'What is the capital of France?',
});

if (response.ok) {
  console.log(response.text);
}

Streaming

for await (const chunk of engine.stream({
  config: { provider: 'ollama', model: 'llama3.2' },
  prompt: 'Tell me a story.',
})) {
  process.stdout.write(chunk);
}

Anthropic or OpenAI-compatible providers

import { AnthropicProvider, OpenAICompatProvider } from '@priest-ai/core';

const engine = new PriestEngine(
  new FilesystemProfileLoader('./profiles'),
  undefined,
  {
    anthropic: new AnthropicProvider('sk-ant-...'),
    openai:    new OpenAICompatProvider('https://api.openai.com', 'sk-...'),
  },
);

const response = await engine.run({
  config: { provider: 'anthropic', model: 'claude-opus-4-6' },
  prompt: 'Summarize the priest protocol in one sentence.',
});

Session Continuity

Pass a session field to persist conversation history across calls.

import { SQLiteSessionStore } from '@priest-ai/core';

const store = new SQLiteSessionStore('./sessions.db');
store.open();

const engine = new PriestEngine(
  new FilesystemProfileLoader('./profiles'),
  store,
  { ollama: new OllamaProvider() },
);

const sessionId = 'user-123-chat';

// First turn — session is created automatically
await engine.run({
  config: { provider: 'ollama', model: 'llama3.2' },
  prompt: 'My name is Alex.',
  session: { id: sessionId },
});

// Second turn — session is continued
const r = await engine.run({
  config: { provider: 'ollama', model: 'llama3.2' },
  prompt: 'What is my name?',
  session: { id: sessionId },
});
// r.text → "Your name is Alex."

session field behavior:

| continueExisting | createIfMissing | Result | |--------------------|-------------------|--------| | true (default) | true (default) | Load existing session or create it | | true | false | Load existing or throw SESSION_NOT_FOUND | | false | — | Always create a new session |

The SQLite store is interoperable with the Python priest SqliteSessionStore and the Swift SQLiteSessionStore — the schema and timestamp format are identical across all implementations.


Profiles

A profile supplies identity, rules, and optional custom and memories that shape the system prompt.

profiles/
├── default.json
└── coder.json
const loader = new FilesystemProfileLoader('./profiles');

Falls back to the built-in default profile when the named file is not found. Use new FilesystemProfileLoader('./profiles', { includeMemories: false }) when the host app owns memory selection and passes selected memory through PriestRequest.memory.

Profile format — default.json:

{
  "identity": "You are a helpful assistant.",
  "rules": "Be honest. Do not make things up.\nBe concise unless the user asks for depth.",
  "memories": []
}

Memory and Context

const response = await engine.run({
  config: { provider: 'ollama', model: 'llama3.2' },
  prompt: 'What should I work on today?',

  // Raw system context — injected first, never trimmed or deduped
  context: ['Today is Monday. App: ProjectManager'],

  // Dynamic memory — deduped against profile memories and each other
  memory: ['User prefers bullet points.', 'Active sprint: v3.0'],

  // Per-turn user context — appended to the user message
  user_context: ['Recent tasks: [fix login bug, update README]'],
});

When max_system_chars is set on the config, the engine trims memory entries tail-first, then profile.memories tail-first. context, rules, identity, custom, and format instructions are never trimmed.

const response = await engine.run({
  config: { provider: 'ollama', model: 'llama3.2', max_system_chars: 4096 },
  prompt: 'Summarize my notes.',
  memory: longMemoryList,
});

Output Format Hints

const response = await engine.run({
  config: { provider: 'ollama', model: 'llama3.2' },
  prompt: 'List three planets as JSON.',
  output: { providerFormat: 'json', promptFormat: 'json' },
});

providerFormat activates the provider's native JSON mode. promptFormat injects a natural-language instruction into the system prompt.

For strict schema compliance, use jsonSchema instead:

const response = await engine.run({
  config: { provider: 'openai', model: 'gpt-4o-mini' },
  prompt: 'Give me a person object.',
  output: {
    jsonSchema: {
      type: 'object',
      properties: { name: { type: 'string' }, age: { type: 'integer' } },
      required: ['name', 'age'],
    },
    jsonSchemaName: 'person',   // optional, defaults to "response"
    jsonSchemaStrict: false,    // true requires additionalProperties:false on all objects
  },
});

jsonSchema maps to response_format:{type:"json_schema"} for OpenAI-compat, format:<schema> for Ollama (v0.5+), and system message injection for Anthropic. It takes precedence over providerFormat when both are set.

response.text is always the raw string. @priest-ai/core never parses the output.


Error Handling

Two errors are always thrown and never captured into response.error:

  • PROVIDER_NOT_REGISTERED — no adapter found for the requested provider key.
  • SESSION_NOT_FOUND — session lookup failed and createIfMissing is false.

All other provider errors (network failures, rate limits, timeouts) are caught and placed into response.error. Check response.ok before reading response.text.

import { PriestError } from '@priest-ai/core';

try {
  const response = await engine.run(request);
  if (response.ok) {
    console.log(response.text);
  } else {
    console.error('Provider error:', response.error?.message);
  }
} catch (err) {
  if (err instanceof PriestError) {
    // PROVIDER_NOT_REGISTERED or SESSION_NOT_FOUND
    console.error('Fatal:', err.code, err.message);
  }
}

Providers

| Key | Class | Notes | |-----|-------|-------| | any | OllamaProvider | NDJSON streaming; local by default (http://localhost:11434) | | any | AnthropicProvider | SSE streaming; requires API key | | any | OpenAICompatProvider | SSE streaming; works with any OpenAI-compatible endpoint |

Provider keys are arbitrary strings — the key you register in the adapters map must match the provider field in the request config.


Custom Providers

Implement ProviderAdapter to add your own backend:

import { ProviderAdapter, Message, AdapterResult, PriestConfig, OutputSpec } from '@priest-ai/core';

class MyProvider implements ProviderAdapter {
  async complete(messages: Message[], config: PriestConfig): Promise<AdapterResult> {
    // call your API
    return { text: '...', finishReason: 'stop' };
  }

  async *stream(messages: Message[], config: PriestConfig): AsyncGenerator<string> {
    yield 'chunk1';
    yield 'chunk2';
  }
}

const engine = new PriestEngine(loader, store, { my: new MyProvider() });

Spec

@priest-ai/core targets priest protocol spec v2.3.0. The spec lives in the priest repository under spec/.

PriestEngine.specVersion  // '2.3.0'

Requirements

  • Node.js 18+
  • TypeScript 5+ (if using TypeScript)
  • better-sqlite3 is the only runtime dependency (required for SQLiteSessionStore; tree-shaken if unused in bundler setups)