Aviary
Packages

@dudousxd/nestjs-agent-testing

In-memory store, governance queries, quota, token sink, and a deterministic fake model — the whole agent loop, offline.

Every SPI the agent depends on, in-memory: AgentStore, AgentGovernanceQueries, QuotaStore, TokenStreamSink, and a scripted ModelProvider. Exercise auto-executing reads, a suspending action tool, and multi-agent delegation with no API key, no database, and no Redis.

pnpm add -D @dudousxd/nestjs-agent-testing
npm install -D @dudousxd/nestjs-agent-testing

Minimal example

import { AgentModule, HeaderActorResolver } from '@dudousxd/nestjs-agent';
import { FakeModelProvider, InMemoryAgentStore, echoScript } from '@dudousxd/nestjs-agent-testing';

AgentModule.forRoot({
  model: new FakeModelProvider(echoScript('Hello from the fake model.')),
  store: new InMemoryAgentStore(), // pass directly instead of importing a store module
  defaultRoles: ['ADMIN'],
  actorResolver: new HeaderActorResolver(),
});

Script a tool call instead of a plain reply by returning toolCall from a FakeScript — the function receives (args, turnIndex), so the script is a pure function of the turn history:

import { FakeModelProvider, type FakeScript } from '@dudousxd/nestjs-agent-testing';

const script: FakeScript = (args, turnIndex) =>
  turnIndex === 0
    ? { text: '', toolCall: { name: 'getWeather', input: { city: 'Lisbon' } } }
    : { text: "It's partly cloudy in Lisbon." };

const model = new FakeModelProvider(script);

Exports

ExportKindPurpose
FakeModelProviderclass (ModelProvider)Deterministic, offline model — streams a scripted reply to the sink and optionally requests one tool call
FakeScripttype(args: ModelTurnArgs, turnIndex: number) => FakeTurn — a pure function of the turn history
FakeTurntype{ text: string; toolCall?: { name: string; input: unknown }; costUsd?: number }
echoScript(reply?)function → FakeScriptTrivial script: stream a fixed reply, never call a tool
InMemoryAgentStoreclass (AgentStore)Full in-memory store — threads, messages, tool calls, usage, and run outcomes (recordRunStart/recordRunEnd/bumpRunRetries) — no database
InMemoryGovernanceQueriesclass (AgentGovernanceQueries)In-memory read-model over an InMemoryAgentStore — spend, usage trend, recent activity, run reliability (runMetrics, runsByAgent, runErrors, runTrend, recentRuns), the approvals inbox (pendingApprovals), tool stats (toolStats), and paged reads (toolCallsPage/threadsPage/runsPage); takes an optional pricing map
InMemoryModelPricetype{ inputPricePer1m; outputPricePer1m; cacheWritePricePer1m?; cacheReadPricePer1m? } — one entry in the pricing map
InMemoryPricingStoreclass (AgentPricingStore)In-memory upsertModelPrice / listCurrentPrices — the write side of model pricing, for tests that assert on seedModelPrices behavior without a database
InMemoryQuotaStoreclass (QuotaStore)In-memory per-actor/day token budget; constructor takes an optional limitTokens (default 1_000_000)
InMemoryTokenStreamSinkclass (TokenStreamSink)Buffers streamed chunks per run so a late subscriber (reconnect) replays everything emitted so far, then follows live

Pricing map defaults to empty — zero cost, tokens still counted

InMemoryGovernanceQueries takes an optional ReadonlyMap<string, InMemoryModelPrice>. Omit it (the default) and every row's estimated cost is 0 while inputTokens / outputTokens still accumulate correctly — pass a map only when a test needs to assert on spend, not just token counts.

import { InMemoryAgentStore, InMemoryGovernanceQueries } from '@dudousxd/nestjs-agent-testing';

const store = new InMemoryAgentStore();
const queries = new InMemoryGovernanceQueries(
  store,
  new Map([['claude-sonnet-4-6', { inputPricePer1m: 3, outputPricePer1m: 15 }]]),
);

InMemoryAgentStore records run start/end and retries the same way a real store does, so queries.runMetrics(range), .recentRuns(limit), and .pendingApprovals(limit) are assertable in a unit test with no database — useful for testing the Reliability/Approvals dashboard sections, or a tool's HITL approval flow, entirely offline.

Peer dependencies

@dudousxd/nestjs-agent-core (workspace peer).

The runnable offline demo

The repo's examples/agent-demo is built entirely on these in-memory pieces — a scripted proof of auto-executing reads, a suspending action tool, and multi-agent delegation, with zero infrastructure:

pnpm --filter agent-demo demo   # the offline scripted proof
pnpm --filter agent-demo start  # the full NestJS app + console at /ai-gateway

When to use it

Use this package for unit tests of your tools and app wiring, and for any environment where a real model/database/Redis would be overkill — CI, demos, local development before you've wired persistence. Swap in a real store adapter (store-mikro-orm or store-drizzle) and a real ModelProvider (@dudousxd/nestjs-agent-ai-sdk's aiSdkModel) for production — the agent loop and your tools are unchanged either way.

  • Persistence — the AgentStore SPI this package's in-memory store implements, and the two real adapters
  • Getting Started — the pnpm --filter agent-demo demo walkthrough

On this page