irzix/nestjs-agentic

The NestJS-native runtime for governed AI agents

TypeScript

125

214 commits

updated Aug 30, 2026

See the code

README

nestjs-agentic banner

nestjs-agentic

The NestJS-native runtime for governed AI agents
Define agents and tools with NestJS, enforce policy before side effects, and keep model integrations replaceable.

NestJS NPM Version CI Status TypeScript License

Why nestjs-agentic?

Most agent frameworks introduce a separate runtime and application boundary. nestjs-agentic keeps agent-facing capabilities inside the NestJS module and dependency-injection system:

NestJS service
    -> @ToolSet and @Tool
    -> context-bound ResolvedTool
    -> allow / deny / require_approval policy decision
    -> RuntimeAdapter

Application services remain ordinary NestJS providers. The model runtime receives governed tool closures rather than direct access to services or application-owned security context.

Current Capabilities

The current release line is 0.6.x. Core primitives, persistence adapters, and durable execution checkpoints are production-intent; higher-order orchestration packages remain experimental while their contracts stabilize.

AreaStatusScope
Agents, tools, and NestJS DIAvailableDecorators, discovery, feature registration, and context-bound tools.
Tool governance & HITLAvailableallow, deny, and require_approval before execution; resumes durably via ApprovalStore.
Model Context Protocol (MCP)Available@nestjs-agentic/mcp for Stdio and SSE remote tool discovery, authorization, and execution.
Built-in runtime & Model CascadingAvailableLoop execution, streaming, budgets, and FrugalGPT confidence-threshold model cascading.
OpenAI & Chat-Completions adapterAvailable@nestjs-agentic/openai for OpenAI, Azure, Ollama, vLLM, Groq, and OpenRouter.
Cognitive Memory & SOP PlaybooksAvailable@nestjs-agentic/memory for Stanford Tri-Factor scoring, SOP playbooks, and reflection.
U-Shaped Context AssemblerAvailable@nestjs-agentic/rag & @nestjs-agentic/core for Lost-in-the-Middle attention mitigation.
Codebase AST & GraphRAGAvailable@nestjs-agentic/rag for AST code splitting, hybrid vector store, and graph traversal.
Debiased Evaluation & Trajectory MetricsAvailable@nestjs-agentic/evaluation for MT-Bench position-debiased judge and AgentBench metrics.
Persistence & Durable CheckpointsAvailableIn-memory, Redis, and PostgreSQL drivers for Session, State, Approval, and Idempotency.
Sub-Agent OrchestrationAvailable@nestjs-agentic/orchestration for parallel delegation, bounded concurrency, and refinement.

See the product roadmap for milestones and production-readiness criteria.

Packages

PackagePurpose
nestjs-agenticMeta package that re-exports the core framework
@nestjs-agentic/coreAgents, tools, policies, approvals, the built-in runtime, and the adapter contracts
@nestjs-agentic/mcpModel Context Protocol (MCP) client transport and tool provider
@nestjs-agentic/openaiOpenAI ModelAdapter, also covering Chat Completions compatible endpoints
@nestjs-agentic/memoryStanford Tri-Factor cognitive scoring, procedural SOP playbooks, and experience reflection
@nestjs-agentic/ragRetrieval strategies, vector stores, and knowledge-graph primitives
@nestjs-agentic/orchestrationSub-agent delegation, parallel execution, and refinement loops
@nestjs-agentic/evaluationMetrics, benchmark execution, and reporting

Installation

npm install nestjs-agentic

Connect a model provider:

npm install @nestjs-agentic/openai openai

Optional packages:

npm install @nestjs-agentic/mcp
npm install @nestjs-agentic/memory
npm install @nestjs-agentic/rag @nestjs-agentic/memory
npm install @nestjs-agentic/orchestration
npm install @nestjs-agentic/evaluation

Quick Start

The example uses MockModelAdapter, so the full tool-calling loop runs deterministically without an API key. Swap in your own ModelAdapter to talk to a real provider.

1. Define a policy and tool set

import { Injectable } from '@nestjs/common';
import {
  AgentContext,
  Context,
  Param,
  PolicyResult,
  Tool,
  ToolPolicy,
  ToolSet,
  UsePolicies,
} from 'nestjs-agentic';

@Injectable()
export class RefundLimitPolicy implements ToolPolicy {
  async evaluate(
    _ctx: AgentContext,
    _toolName: string,
    args: Record<string, unknown>,
  ): Promise<PolicyResult> {
    return Number(args.amount) > 500
      ? { decision: 'require_approval', reason: 'Refund exceeds $500.' }
      : { decision: 'allow' };
  }
}

@ToolSet({ name: 'orders' })
export class OrderTools {
  @Tool({ name: 'refundOrder', description: 'Refund an order' })
  @UsePolicies(RefundLimitPolicy)
  async refundOrder(
    @Param('orderId') orderId: string,
    @Param('amount', { type: 'number' }) amount: number,
    @Context() ctx: AgentContext,
  ) {
    return { orderId, amount, requestedBy: ctx.security.userId };
  }
}

2. Define an agent and module

import { Module } from '@nestjs/common';
import {
  Agent,
  AgentConfig,
  AgenticModule,
  AgentProvider,
  MockModelAdapter,
} from 'nestjs-agentic';

@Agent({ name: 'support', description: 'Handles support requests' })
export class SupportAgent implements AgentProvider {
  constructor(private readonly orderTools: OrderTools) {}

  define(): AgentConfig {
    return {
      instructions: 'Help the user while respecting tool policies.',
      tools: [this.orderTools],
    };
  }
}

const model = new MockModelAdapter();
model
  .whenAsked('Refund $600 for order #42')
  .callTool('refundOrder', { orderId: '42', amount: 600 })
  .reply('That refund needs approval before I can complete it.');

@Module({
  imports: [
    AgenticModule.forRoot({
      defaultModel: { provider: 'mock', model: 'deterministic' },
      modelAdapter: model,
      limits: { maxIterations: 6 },
    }),
    AgenticModule.forFeature({
      agents: [SupportAgent],
      toolSets: [OrderTools],
      policies: [RefundLimitPolicy],
    }),
  ],
})
export class AppModule {}

AgenticModule.forFeature() registers these classes inside AgenticModule. Keep an agent, its tool sets, and its policies in a single forFeature() call, and export any application services they inject from a @Global() module.

3. Run the agent and handle approval

import { Body, Controller, Param, Post } from '@nestjs/common';
import { AgentRunner, ApprovalService } from 'nestjs-agentic';

@Controller('support')
export class SupportController {
  constructor(
    private readonly runner: AgentRunner,
    private readonly approvals: ApprovalService,
  ) {}

  @Post('chat')
  chat(@Body() body: { sessionId: string; message: string }) {
    return this.runner.run('support', {
      sessionId: body.sessionId,
      message: body.message,
      context: {
        userId: 'user_123',
        tenantId: 'acme',
      },
    });
  }

  @Post('approve/:id')
  approve(@Param('id') id: string) {
    return this.approvals.approve(id);
  }

  @Post('reject/:id')
  reject(@Param('id') id: string) {
    return this.approvals.reject(id);
  }
}

runner.runStream() exposes structured token, tool_start, tool_result, approval_required, and complete events.

Each run is bounded. Pass limits and a signal to cap iterations, tool calls, tokens, and wall-clock time, or to cancel work in flight:

await runner.run('support', {
  sessionId,
  message,
  limits: { maxIterations: 4, maxToolCalls: 8, timeoutMs: 30_000 },
  signal: abortController.signal,
});

Built-in Policies

  • RateLimitPolicy — process-local sliding-window limits by tenant, user, and tool.
  • CostLimitPolicy — numeric allow, approval, and deny thresholds.
  • LoggingPolicy — configurable tool-attempt logging with field masking.

These are framework primitives, not replacements for distributed rate limiting, durable audit storage, or application authorization.

Connecting a Model

For OpenAI and any Chat Completions compatible endpoint, use the published adapter:

import { AgenticModule } from 'nestjs-agentic';
import { OpenAiModelAdapter } from '@nestjs-agentic/openai';

AgenticModule.forRoot({
  defaultModel: { provider: 'openai', model: 'gpt-4o-mini' },
  modelAdapter: new OpenAiModelAdapter({ apiKey: process.env.OPENAI_API_KEY }),
});

The same adapter targets local and third-party servers by pointing baseUrl at them, for example http://localhost:11434/v1 for Ollama. See @nestjs-agentic/openai for Azure, reasoning models, and compatibility notes.

For any other provider, implement ModelAdapter directly. It handles only provider communication; the framework owns the loop, validation, policies, budgets, and streaming.

import type { ModelAdapter, ModelRequest, ModelResponse } from 'nestjs-agentic';

export class MyModelAdapter implements ModelAdapter {
  async generate(request: ModelRequest): Promise<ModelResponse> {
    const completion = await callProvider({
      model: request.model.model,
      messages: request.messages,
      tools: request.tools,
      signal: request.signal,
    });

    return {
      content: completion.text,
      toolCalls: completion.toolCalls,
      usage: completion.usage,
      finishReason: completion.toolCalls.length ? 'tool_calls' : 'stop',
    };
  }
}

The core package does not import external model SDKs. Custom model adapters implement ModelAdapter directly, while the framework manages loop execution, policy enforcement, budgets, and streaming.

Documentation

Runnable examples are available in examples.

Sponsorship & Support

nestjs-agentic is an open-source framework dedicated to production-grade, governed AI agent systems in NestJS. If you or your organization find value in the project, consider supporting ongoing development:

License

MIT © irzix

agent
agentic
agentic-ai
nestjs
nestjs-backend

Contributors

irzix

206 commits

hamedkashani

1 commits

shiva-hs

1 commits

irzix/nestjs-agentic

The NestJS-native runtime for governed AI agents

TypeScript

125

214 commits

updated Aug 30, 2026

See the code

README

nestjs-agentic banner

nestjs-agentic

The NestJS-native runtime for governed AI agents
Define agents and tools with NestJS, enforce policy before side effects, and keep model integrations replaceable.

NestJS NPM Version CI Status TypeScript License

Why nestjs-agentic?

Most agent frameworks introduce a separate runtime and application boundary. nestjs-agentic keeps agent-facing capabilities inside the NestJS module and dependency-injection system:

NestJS service
    -> @ToolSet and @Tool
    -> context-bound ResolvedTool
    -> allow / deny / require_approval policy decision
    -> RuntimeAdapter

Application services remain ordinary NestJS providers. The model runtime receives governed tool closures rather than direct access to services or application-owned security context.

Current Capabilities

The current release line is 0.6.x. Core primitives, persistence adapters, and durable execution checkpoints are production-intent; higher-order orchestration packages remain experimental while their contracts stabilize.

AreaStatusScope
Agents, tools, and NestJS DIAvailableDecorators, discovery, feature registration, and context-bound tools.
Tool governance & HITLAvailableallow, deny, and require_approval before execution; resumes durably via ApprovalStore.
Model Context Protocol (MCP)Available@nestjs-agentic/mcp for Stdio and SSE remote tool discovery, authorization, and execution.
Built-in runtime & Model CascadingAvailableLoop execution, streaming, budgets, and FrugalGPT confidence-threshold model cascading.
OpenAI & Chat-Completions adapterAvailable@nestjs-agentic/openai for OpenAI, Azure, Ollama, vLLM, Groq, and OpenRouter.
Cognitive Memory & SOP PlaybooksAvailable@nestjs-agentic/memory for Stanford Tri-Factor scoring, SOP playbooks, and reflection.
U-Shaped Context AssemblerAvailable@nestjs-agentic/rag & @nestjs-agentic/core for Lost-in-the-Middle attention mitigation.
Codebase AST & GraphRAGAvailable@nestjs-agentic/rag for AST code splitting, hybrid vector store, and graph traversal.
Debiased Evaluation & Trajectory MetricsAvailable@nestjs-agentic/evaluation for MT-Bench position-debiased judge and AgentBench metrics.
Persistence & Durable CheckpointsAvailableIn-memory, Redis, and PostgreSQL drivers for Session, State, Approval, and Idempotency.
Sub-Agent OrchestrationAvailable@nestjs-agentic/orchestration for parallel delegation, bounded concurrency, and refinement.

See the product roadmap for milestones and production-readiness criteria.

Packages

PackagePurpose
nestjs-agenticMeta package that re-exports the core framework
@nestjs-agentic/coreAgents, tools, policies, approvals, the built-in runtime, and the adapter contracts
@nestjs-agentic/mcpModel Context Protocol (MCP) client transport and tool provider
@nestjs-agentic/openaiOpenAI ModelAdapter, also covering Chat Completions compatible endpoints
@nestjs-agentic/memoryStanford Tri-Factor cognitive scoring, procedural SOP playbooks, and experience reflection
@nestjs-agentic/ragRetrieval strategies, vector stores, and knowledge-graph primitives
@nestjs-agentic/orchestrationSub-agent delegation, parallel execution, and refinement loops
@nestjs-agentic/evaluationMetrics, benchmark execution, and reporting

Installation

npm install nestjs-agentic

Connect a model provider:

npm install @nestjs-agentic/openai openai

Optional packages:

npm install @nestjs-agentic/mcp
npm install @nestjs-agentic/memory
npm install @nestjs-agentic/rag @nestjs-agentic/memory
npm install @nestjs-agentic/orchestration
npm install @nestjs-agentic/evaluation

Quick Start

The example uses MockModelAdapter, so the full tool-calling loop runs deterministically without an API key. Swap in your own ModelAdapter to talk to a real provider.

1. Define a policy and tool set

import { Injectable } from '@nestjs/common';
import {
  AgentContext,
  Context,
  Param,
  PolicyResult,
  Tool,
  ToolPolicy,
  ToolSet,
  UsePolicies,
} from 'nestjs-agentic';

@Injectable()
export class RefundLimitPolicy implements ToolPolicy {
  async evaluate(
    _ctx: AgentContext,
    _toolName: string,
    args: Record<string, unknown>,
  ): Promise<PolicyResult> {
    return Number(args.amount) > 500
      ? { decision: 'require_approval', reason: 'Refund exceeds $500.' }
      : { decision: 'allow' };
  }
}

@ToolSet({ name: 'orders' })
export class OrderTools {
  @Tool({ name: 'refundOrder', description: 'Refund an order' })
  @UsePolicies(RefundLimitPolicy)
  async refundOrder(
    @Param('orderId') orderId: string,
    @Param('amount', { type: 'number' }) amount: number,
    @Context() ctx: AgentContext,
  ) {
    return { orderId, amount, requestedBy: ctx.security.userId };
  }
}

2. Define an agent and module

import { Module } from '@nestjs/common';
import {
  Agent,
  AgentConfig,
  AgenticModule,
  AgentProvider,
  MockModelAdapter,
} from 'nestjs-agentic';

@Agent({ name: 'support', description: 'Handles support requests' })
export class SupportAgent implements AgentProvider {
  constructor(private readonly orderTools: OrderTools) {}

  define(): AgentConfig {
    return {
      instructions: 'Help the user while respecting tool policies.',
      tools: [this.orderTools],
    };
  }
}

const model = new MockModelAdapter();
model
  .whenAsked('Refund $600 for order #42')
  .callTool('refundOrder', { orderId: '42', amount: 600 })
  .reply('That refund needs approval before I can complete it.');

@Module({
  imports: [
    AgenticModule.forRoot({
      defaultModel: { provider: 'mock', model: 'deterministic' },
      modelAdapter: model,
      limits: { maxIterations: 6 },
    }),
    AgenticModule.forFeature({
      agents: [SupportAgent],
      toolSets: [OrderTools],
      policies: [RefundLimitPolicy],
    }),
  ],
})
export class AppModule {}

AgenticModule.forFeature() registers these classes inside AgenticModule. Keep an agent, its tool sets, and its policies in a single forFeature() call, and export any application services they inject from a @Global() module.

3. Run the agent and handle approval

import { Body, Controller, Param, Post } from '@nestjs/common';
import { AgentRunner, ApprovalService } from 'nestjs-agentic';

@Controller('support')
export class SupportController {
  constructor(
    private readonly runner: AgentRunner,
    private readonly approvals: ApprovalService,
  ) {}

  @Post('chat')
  chat(@Body() body: { sessionId: string; message: string }) {
    return this.runner.run('support', {
      sessionId: body.sessionId,
      message: body.message,
      context: {
        userId: 'user_123',
        tenantId: 'acme',
      },
    });
  }

  @Post('approve/:id')
  approve(@Param('id') id: string) {
    return this.approvals.approve(id);
  }

  @Post('reject/:id')
  reject(@Param('id') id: string) {
    return this.approvals.reject(id);
  }
}

runner.runStream() exposes structured token, tool_start, tool_result, approval_required, and complete events.

Each run is bounded. Pass limits and a signal to cap iterations, tool calls, tokens, and wall-clock time, or to cancel work in flight:

await runner.run('support', {
  sessionId,
  message,
  limits: { maxIterations: 4, maxToolCalls: 8, timeoutMs: 30_000 },
  signal: abortController.signal,
});

Built-in Policies

  • RateLimitPolicy — process-local sliding-window limits by tenant, user, and tool.
  • CostLimitPolicy — numeric allow, approval, and deny thresholds.
  • LoggingPolicy — configurable tool-attempt logging with field masking.

These are framework primitives, not replacements for distributed rate limiting, durable audit storage, or application authorization.

Connecting a Model

For OpenAI and any Chat Completions compatible endpoint, use the published adapter:

import { AgenticModule } from 'nestjs-agentic';
import { OpenAiModelAdapter } from '@nestjs-agentic/openai';

AgenticModule.forRoot({
  defaultModel: { provider: 'openai', model: 'gpt-4o-mini' },
  modelAdapter: new OpenAiModelAdapter({ apiKey: process.env.OPENAI_API_KEY }),
});

The same adapter targets local and third-party servers by pointing baseUrl at them, for example http://localhost:11434/v1 for Ollama. See @nestjs-agentic/openai for Azure, reasoning models, and compatibility notes.

For any other provider, implement ModelAdapter directly. It handles only provider communication; the framework owns the loop, validation, policies, budgets, and streaming.

import type { ModelAdapter, ModelRequest, ModelResponse } from 'nestjs-agentic';

export class MyModelAdapter implements ModelAdapter {
  async generate(request: ModelRequest): Promise<ModelResponse> {
    const completion = await callProvider({
      model: request.model.model,
      messages: request.messages,
      tools: request.tools,
      signal: request.signal,
    });

    return {
      content: completion.text,
      toolCalls: completion.toolCalls,
      usage: completion.usage,
      finishReason: completion.toolCalls.length ? 'tool_calls' : 'stop',
    };
  }
}

The core package does not import external model SDKs. Custom model adapters implement ModelAdapter directly, while the framework manages loop execution, policy enforcement, budgets, and streaming.

Documentation

Runnable examples are available in examples.

Sponsorship & Support

nestjs-agentic is an open-source framework dedicated to production-grade, governed AI agent systems in NestJS. If you or your organization find value in the project, consider supporting ongoing development:

License

MIT © irzix

agent
agentic
agentic-ai
nestjs
nestjs-backend

Contributors

irzix

206 commits

hamedkashani

1 commits

shiva-hs

1 commits

Languages

TypeScript

88.2%

MDX

6.1%

CSS

5.4%