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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@manifesto-ai/agent

v0.3.0

Published

Session layer that executes LLM as a pure policy function and standardizes all side effects as Effects

Readme

@manifesto-ai/agent

A session layer that executes LLM as a "pure policy function," standardizing all side effects as Effects with Runtime control.

f(snapshot) → effects[]

Overview

@manifesto-ai/agent treats the LLM as a deterministic component—not the master, but a CPU-level policy executor. The Runtime owns all control flow, validates all outputs, and manages all side effects.

Core Principles

| Principle | Description | |-----------|-------------| | LLM as Component | f(snapshot) → effects[] — LLM declares intent, Runtime executes | | Runtime Owns Control | step/run, budget, stop — Control flow always belongs to Runtime | | Enforce, Don't Inject | Schema → Constraints compilation + Validator Gatekeeping | | Errors Become State | No exception crashes — failures are recorded and available for self-correction | | Sequential + Stop-on-Failure | Partial failure discards remaining effects |

Installation

npm install @manifesto-ai/agent
# or
pnpm add @manifesto-ai/agent

Quick Start

import {
  createSimpleSession,
  createMockClient,
  generateEffectId,
  type AgentDecision,
} from '@manifesto-ai/agent';

// Define decisions the mock LLM will return
const decisions: AgentDecision[] = [
  {
    effects: [
      {
        type: 'snapshot.patch',
        id: generateEffectId(),
        ops: [{ op: 'set', path: 'data.status', value: 'processing' }],
      },
      {
        type: 'log.emit',
        id: generateEffectId(),
        level: 'info',
        message: 'Started processing',
      },
    ],
  },
  { effects: [] }, // Empty effects = session complete
];

// Create a simple session
const { session, getSnapshot } = createSimpleSession({
  initialSnapshot: {
    data: { status: 'idle' },
    state: { phase: 'init' },
    derived: {},
  },
  client: createMockClient(decisions),
});

// Run the session
const result = await session.run();

console.log(result);
// { done: true, totalSteps: 2, totalEffects: 2, reason: 'Empty effects' }

console.log(getSnapshot().data.status);
// 'processing'

Core Concepts

Effect Types

Effects are declarations of intent. The LLM declares what it wants; the Runtime decides whether and how to execute.

type Effect =
  | ToolCallEffect      // Call an external tool
  | SnapshotPatchEffect // Modify snapshot state
  | LogEmitEffect;      // Emit a log message

// Tool call
{
  type: 'tool.call',
  id: 'eff_abc123',
  tool: 'search',
  input: { query: 'manifesto architecture' }
}

// Snapshot patch
{
  type: 'snapshot.patch',
  id: 'eff_def456',
  ops: [
    { op: 'set', path: 'data.results', value: [...] },
    { op: 'append', path: 'data.history', value: 'searched' }
  ],
  reason: 'Store search results'
}

// Log emit
{
  type: 'log.emit',
  id: 'eff_ghi789',
  level: 'info',
  message: 'Processing complete'
}

HumanAskEffect (v0.2 Preview)

Type definition only in v0.1. Runtime support planned for v0.2.

type HumanAskEffect = {
  type: 'human.ask';
  id: string;
  question: string;
  options?: string[];
};

PatchOp Operations

Only two operations are allowed in v0.1:

| Operation | Description | Example | |-----------|-------------|---------| | set | Set a value at path | { op: 'set', path: 'data.name', value: 'John' } | | append | Append to array | { op: 'append', path: 'data.items', value: 'new' } |

Path Rules:

  • Dot-separated: "data.user.name"
  • Array indices: "data.items.0.status"
  • 0-based indexing with bounds checking

Forbidden: delete, move, replace, copy

Snapshot Structure & ACL

{
  data: { ... },    // LLM writable
  state: { ... },   // LLM writable (phase, etc.)
  derived: { ... }  // LLM write FORBIDDEN (Runtime managed)
}

The derived.* namespace is exclusively managed by the Runtime. Any LLM attempt to write to derived.* will be rejected with a validation error.

Constraints

Constraints define what the LLM can and cannot do in the current phase:

import {
  createDefaultConstraints,
  addTypeRule,
  addInvariant,
} from '@manifesto-ai/agent';

let constraints = createDefaultConstraints('processing');

// Add type rules
constraints = addTypeRule(constraints, 'data.count', 'number');
constraints = addTypeRule(constraints, 'data.name', 'string');

// Add invariants
constraints = addInvariant(
  constraints,
  'count_positive',
  'Count must be positive'
);

Validation Pipeline

Every snapshot.patch effect goes through a validation pipeline:

PatchOp received
       ↓
1. Schema check (op/path/value structure)
       ↓
2. ACL check (derived.* write forbidden)
       ↓
3. Bounds check (array index range)
       ↓
4. Type rules check (expected types)
       ↓
5. Invariant check (phase-specific rules)
       ↓
   Pass → Apply    Fail → Record Error State

Error as State

When validation fails, errors are recorded as state—not thrown as exceptions:

const errors = getErrors();
// [
//   {
//     kind: 'patch_validation_error',
//     at: 'derived.x',
//     issue: 'Forbidden path',
//     advice: 'derived.* paths are Runtime-managed',
//     effectId: 'eff_abc123',
//     ts: 1704067200000
//   }
// ]

The LLM receives these errors in the next step and can self-correct.

Projection (v0.1.x)

Projection allows you to control what portion of the snapshot is sent to the LLM, managing token budgets and focusing context on relevant data.

Why Projection?

  • Token Budget Management: Large snapshots can exceed LLM context limits
  • Focused Context: Send only relevant data for current task
  • Cost Optimization: Smaller prompts reduce API costs

Using Projection

Simple Configuration

import { createAgentSession } from '@manifesto-ai/agent';

const session = createAgentSession({
  runtime: myRuntime,
  client: myClient,
  projection: {
    paths: ['data.currentItem', 'state.phase', 'derived.observations'],
    tokenBudget: 4000,
    compressionStrategy: 'truncate',
    requiredPaths: ['state.phase'],
    excludePaths: ['data.largeBlob'],
  },
});

Custom Projection Provider

import { createSimpleProjectionProvider } from '@manifesto-ai/agent';

const provider = createSimpleProjectionProvider({
  paths: ['data.currentItem', 'state.phase'],
  config: {
    tokenBudget: 4000,
    compressionStrategy: 'truncate',
  },
});

const session = createAgentSession({
  runtime: myRuntime,
  client: myClient,
  projectionProvider: provider,
});

Dynamic Projection

import { createDynamicProjectionProvider } from '@manifesto-ai/agent';

const provider = createDynamicProjectionProvider({
  pathResolver: (snapshot) => {
    const paths = ['state.phase'];
    if (snapshot.state.phase === 'editing') {
      paths.push('data.currentItem', 'data.editHistory');
    } else if (snapshot.state.phase === 'reviewing') {
      paths.push('data.items', 'derived.summary');
    }
    return paths;
  },
  config: { tokenBudget: 4000 },
});

Identity Projection (No Projection)

import { createIdentityProjectionProvider } from '@manifesto-ai/agent';

// Passes full snapshot through (for small snapshots or testing)
const provider = createIdentityProjectionProvider();

Projection Metadata

The LLM client receives projection metadata along with the snapshot:

type ProjectionMetadata = {
  isProjected: boolean;      // Whether projection was applied
  tokenCount: number;        // Estimated token count
  includedPaths: string[];   // Paths included in projection
  compressed?: boolean;      // Whether compression was applied
  compressionStrategy?: CompressionStrategy;
};

Compression Strategies

| Strategy | Description | |----------|-------------| | truncate | Limit array sizes progressively until under budget | | summarize | (Future) Use LLM to summarize large sections | | prioritize | (Future) Keep high-priority paths, drop low-priority |

Session API

Creating a Session

import {
  createAgentSession,
  createDefaultHandlerRegistry,
  createDefaultConstraints,
} from '@manifesto-ai/agent';

const session = createAgentSession({
  runtime: agentRuntime,  // AgentRuntime implementation
  client: llmClient,      // AgentClient implementation
  policy: {
    maxSteps: 100,
    maxEffectsPerStep: 16,
  },
  handlers: createDefaultHandlerRegistry(),
  compileConstraints: (snapshot) => createDefaultConstraints(snapshot.state.phase),
});

Running a Session

// Single step
const stepResult = await session.step();
// { done: boolean, effectsExecuted: number, errorsEncountered: number }

// Run until completion
const runResult = await session.run();
// { done: boolean, totalSteps: number, totalEffects: number, reason?: string }

Done Checkers

Control when the session completes:

import { phaseDoneChecker } from '@manifesto-ai/agent';

const session = createAgentSession({
  runtime: myRuntime,
  client: myClient,
  isDone: phaseDoneChecker('complete'),
});

// Custom done checker
const session = createAgentSession({
  runtime: myRuntime,
  client: myClient,
  isDone: (snapshot) => ({
    done: snapshot.data.items.length >= 10,
    reason: 'Maximum items reached',
  }),
});

Simple Session (for Testing)

import { createSimpleSession } from '@manifesto-ai/agent';

const { session, getSnapshot, getErrors, getObservations } = createSimpleSession({
  initialSnapshot: { data: {}, state: {}, derived: {} },
  client: yourClient,
  policy: { maxSteps: 50 },
  isDone: (snapshot) => ({
    done: snapshot.state.phase === 'complete',
    reason: 'Phase complete',
  }),
});

AgentRuntime

Using with @manifesto-ai/core

import { createRuntime } from '@manifesto-ai/core';
import { createAgentRuntime, createAgentSession } from '@manifesto-ai/agent';

// Create domain runtime from core
const domainRuntime = createRuntime({ domain: myDomain });

// Wrap with agent runtime adapter
const agentRuntime = createAgentRuntime({
  domainRuntime,
  maxErrors: 100,
  maxObservations: 1000,
});

// Create session with agent runtime
const session = createAgentSession({
  runtime: agentRuntime,
  client: myClient,
});

AgentRuntime Interface

interface AgentRuntime<S> {
  getSnapshot(): S;
  applyPatch(ops: PatchOp[]): ApplyResult<S>;
  appendError(error: ErrorState): void;
  getRecentErrors(limit?: number): PatchErrorState[];
  clearErrors(): void;
  appendObservation(obs: Observation): void;
}

Note: ManifestoCoreLike is deprecated. Use AgentRuntime instead.

Tools

Defining Tools

import { defineTool, createToolRegistry } from '@manifesto-ai/agent';
import { z } from 'zod';

// defineTool(name, inputSchema, execute)
const searchTool = defineTool(
  'search',
  z.object({ query: z.string() }),
  async (input) => {
    const results = await performSearch(input.query);
    return { results };
  }
);

const calculatorTool = defineTool(
  'calculate',
  z.object({ expression: z.string() }),
  async (input) => {
    return { result: eval(input.expression) };
  }
);

const toolRegistry = createToolRegistry([searchTool, calculatorTool]);

Tool Results as Observations

When a tool executes, its result is automatically pushed to derived.observations:

// After tool.call effect executes:
snapshot.derived.observations = [
  {
    id: 'obs_xyz789',
    source: 'tool:search',
    content: { results: [...] },
    triggeredBy: 'eff_abc123',
    ts: 1704067200000
  }
];

Prompt Building

System Prompt

import { SYSTEM_PROMPT, buildSystemPrompt } from '@manifesto-ai/agent';

// Base system prompt with Iron Laws
console.log(SYSTEM_PROMPT);

// With tools and context
const prompt = buildSystemPrompt({
  includeToolList: true,
  tools: [
    { name: 'search', description: 'Search the web' },
  ],
  additionalContext: 'You are working on a todo application.',
});

Step Prompt

import { buildStepPrompt, createDefaultConstraints } from '@manifesto-ai/agent';

const prompt = buildStepPrompt({
  snapshot: currentSnapshot,
  constraints: createDefaultConstraints('processing'),
  recentErrors: errors,
  instruction: 'Process the next item in the queue.',
});

AgentClient Interface

Implement this interface to connect your LLM:

import type { AgentClient, AgentDecision, Constraints } from '@manifesto-ai/agent';

const myClient: AgentClient = {
  async decide(input) {
    const { snapshot, constraints, recentErrors, instruction, projectionMeta } = input;

    // Call your LLM here
    const response = await callLLM({
      systemPrompt: buildSystemPrompt(),
      userPrompt: buildStepPrompt({
        snapshot,
        constraints,
        recentErrors,
        instruction,
      }),
    });

    // Parse and return AgentDecision
    return JSON.parse(response) as AgentDecision;
  },
};

AgentDecision Schema

type AgentDecision = {
  effects: Effect[];
  trace?: {
    model?: string;
    tokensIn?: number;
    tokensOut?: number;
    raw?: unknown;
  };
};

AgentClientInput

type AgentClientInput<S = unknown> = {
  snapshot: S;
  constraints: Constraints;
  recentErrors?: PatchErrorState[];
  instruction?: string;
  projectionMeta?: ProjectionMetadata;  // Included when using projection
};

Testing

Mock Client

import { createMockClient, generateEffectId } from '@manifesto-ai/agent';

const decisions = [
  {
    effects: [
      {
        type: 'snapshot.patch',
        id: generateEffectId(),
        ops: [{ op: 'set', path: 'data.x', value: 1 }],
      },
    ],
  },
  { effects: [] },
];

const client = createMockClient(decisions);

Fixed Client

import { createFixedClient, generateEffectId } from '@manifesto-ai/agent';

// Returns the same effects every time
const client = createFixedClient([
  {
    type: 'log.emit',
    id: generateEffectId(),
    level: 'info',
    message: 'Hello',
  },
]);

Invariant Helpers

import {
  requiredFieldInvariant,
  rangeInvariant,
  arrayLengthInvariant,
  customInvariant,
} from '@manifesto-ai/agent';

// Field must exist and not be null/undefined
const hasName = requiredFieldInvariant('data.user.name');

// Number must be in range
const validAge = rangeInvariant('data.user.age', 0, 150);

// Array length constraint
const maxItems = arrayLengthInvariant('data.items', 0, 100);

// Custom validation
const customRule = customInvariant(
  'positive_balance',
  'Balance must be positive',
  (snapshot) => snapshot.data.balance > 0
);

Error Types

import type {
  PatchErrorState,
  EffectErrorState,
  HandlerErrorState,
} from '@manifesto-ai/agent';

// Patch validation error
type PatchErrorState = {
  kind: 'patch_validation_error';
  at: string;           // Problem path
  issue: string;        // Error description
  expected?: unknown;   // Expected type/value
  got?: unknown;        // Actual type/value
  advice?: string;      // Correction hint
  effectId: string;
  ts: number;
};

Configuration

Policy

type Policy = {
  maxSteps: number;           // Maximum steps before forced stop
  maxEffectsPerStep?: number; // Default: 16
};

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     @manifesto-ai/agent                      │
│   (LLM policy execution + Effect standardization +           │
│       Projection + Runtime enforcement)                      │
└─────────────────────────┬───────────────────────────────────┘
                          │ uses
┌─────────────────────────▼───────────────────────────────────┐
│                     @manifesto-ai/core                       │
│       (Snapshot storage/transition/logging —                 │
│              Constitution + minimal infra)                   │
└─────────────────────────────────────────────────────────────┘

API Reference

Types

  • Effect, ToolCallEffect, SnapshotPatchEffect, LogEmitEffect, HumanAskEffect
  • PatchOp
  • Constraints, TypeRule, Invariant
  • Policy
  • PatchErrorState, EffectErrorState, HandlerErrorState, ErrorState
  • AgentClient, AgentDecision, AgentClientInput
  • AgentSession, StepResult, RunResult
  • AgentRuntime, ApplyResult, DoneChecker
  • Observation
  • Tool, ToolRegistry
  • ProjectedSnapshot, ProjectionMetadata, ProjectionResult
  • CompressionStrategy, ProjectionProviderConfig, ProjectionProvider

Session

  • createAgentSession(options) - Create a full session
  • createSimpleSession(options) - Create a simple session for testing
  • createAgentRuntime(options) - Create AgentRuntime from DomainRuntime

Projection

  • createSimpleProjectionProvider(options) - Path-based projection
  • createIdentityProjectionProvider(config?) - No projection (passthrough)
  • createDynamicProjectionProvider(options) - Dynamic path resolution

Validation

  • validatePathAcl(path, effectId) - Check write ACL
  • validatePathBounds(path, snapshot, effectId) - Check array bounds
  • validateTypeRule(path, value, rule, effectId) - Check type rule
  • validateInvariant(snapshot, invariant) - Check invariant
  • validatePatchPipeline(ops, snapshot, constraints, effectId) - Full validation

Handlers

  • createDefaultHandlerRegistry() - Create registry with default handlers
  • createToolCallHandler(tools, core) - Create tool call handler
  • createSnapshotPatchHandler(core, constraints) - Create patch handler
  • createLogEmitHandler(collector?) - Create log handler

Prompt

  • SYSTEM_PROMPT - Base system prompt with Iron Laws
  • buildSystemPrompt(options?) - Build customized system prompt
  • buildStepPrompt(input, options?) - Build per-step prompt
  • buildLLMMessages(systemPrompt, stepInput) - Build message array

Utilities

  • generateEffectId() - Generate unique effect ID
  • generateObservationId() - Generate unique observation ID
  • createDefaultConstraints(phase?) - Create default constraints
  • createMockClient(decisions) - Create mock client for testing
  • createFixedClient(effects) - Create fixed-response client
  • defaultDoneChecker() - Always returns not done
  • phaseDoneChecker(targetPhase) - Done when phase matches

Tools

  • defineTool(name, inputSchema, execute) - Define a tool with name, Zod schema, and handler
  • createToolRegistry(tools) - Create a registry from tool array

Invariant Helpers

  • requiredFieldInvariant(path) - Field must exist
  • rangeInvariant(path, min, max) - Number in range
  • arrayLengthInvariant(path, min, max) - Array length constraint
  • customInvariant(id, message, predicate) - Custom validation

License

MIT

Contributing

See CONTRIBUTING.md for guidelines.

Related Packages