
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
@juspay-jaf/jaf
Advanced tools
Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

A purely functional agent framework built on immutable state, type safety, and composable policies. JAF enables building production-ready AI agent systems with built-in security, observability, and error handling.
readonly# Install from npm
npm install @juspay-jaf/jaf
# Or using yarn
yarn add @juspay-jaf/jaf
# Or using pnpm
pnpm add @juspay-jaf/jaf
# Clone the repository
git clone https://github.com/xynehq/jaf.git
cd jaf
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
src/
├── core/ # Core framework types and engine
│ ├── engine.ts # Main execution engine
│ ├── errors.ts # Error handling and types
│ ├── tool-results.ts # Tool execution results
│ ├── tracing.ts # Event tracing system
│ └── types.ts # Core type definitions
├── memory/ # Memory providers for conversation persistence
│ ├── factory.ts # Memory provider factory
│ ├── types.ts # Memory system types
│ └── providers/
│ ├── in-memory.ts # In-memory provider
│ ├── postgres.ts # PostgreSQL provider
│ └── redis.ts # Redis provider
├── providers/ # External integrations
│ ├── mcp.ts # Model Context Protocol integration
│ └── model.ts # LLM provider integrations
├── policies/ # Validation and security policies
│ ├── handoff.ts # Agent handoff policies
│ └── validation.ts # Input/output validation
├── server/ # HTTP server implementation
│ ├── index.ts # Server entry point
│ ├── server.ts # Express server setup
│ └── types.ts # Server-specific types
├── __tests__/ # Test suite
│ ├── engine.test.ts # Engine tests
│ └── validation.test.ts # Validation tests
└── index.ts # Main framework exports
examples/
├── rag-demo/ # Vertex AI RAG integration demo
│ ├── index.ts # Demo entry point
│ ├── rag-agent.ts # RAG agent implementation
│ └── rag-tool.ts # RAG tool implementation
├── hooks/turn-end-review.ts # Demonstrates awaiting onTurnEnd reviews between turns
└── server-demo/ # Development server demo
└── index.ts # Server demo entry point
docs/ # Documentation
├── getting-started.md
├── core-concepts.md
├── api-reference.md
├── tools.md
├── memory-system.md
├── model-providers.md
├── server-api.md
├── examples.md
├── deployment.md
└── troubleshooting.md
import { z } from 'zod';
import { Agent, Tool, RunState, run } from '@juspay-jaf/jaf';
// Define your context type
type MyContext = {
userId: string;
permissions: string[];
};
// Create a tool
const calculatorTool: Tool<{ expression: string }, MyContext> = {
schema: {
name: "calculate",
description: "Perform mathematical calculations",
parameters: z.object({
expression: z.string().describe("Math expression to evaluate")
}),
},
execute: async (args) => {
const result = eval(args.expression); // Don't do this in production!
return `${args.expression} = ${result}`;
},
};
// Define an agent
const mathAgent: Agent<MyContext, string> = {
name: 'MathTutor',
instructions: () => 'You are a helpful math tutor',
tools: [calculatorTool],
};
import { run, makeLiteLLMProvider } from '@juspay-jaf/jaf';
const modelProvider = makeLiteLLMProvider('http://localhost:4000');
const agentRegistry = new Map([['MathTutor', mathAgent]]);
const config = {
agentRegistry,
modelProvider,
maxTurns: 10,
onEvent: (event) => console.log(event), // Real-time tracing
onTurnEnd: async ({ turn, lastAssistantMessage }) => {
console.log(`Turn ${turn} completed:`, lastAssistantMessage?.content);
// Run reviews, persist breadcrumbs, throttle next turn, etc.
},
};
const initialState = {
runId: generateRunId(),
traceId: generateTraceId(),
messages: [{ role: 'user', content: 'What is 2 + 2?' }],
currentAgentName: 'MathTutor',
context: { userId: 'user123', permissions: ['user'] },
turnCount: 0,
};
const result = await run(initialState, config);
JAF emphasizes function composition to build complex behaviors from simple, reusable functions:
import { createFunctionTool, composeTool, withRetry, withCache } from '@juspay-jaf/jaf';
// Simple base tools
const fetchWeatherTool = createFunctionTool({
name: 'fetch_weather',
description: 'Fetch weather data',
execute: async ({ location }) => {
const response = await fetch(`/api/weather?location=${location}`);
return response.json();
},
parameters: [{ name: 'location', type: 'string', required: true }]
});
const formatTemperatureTool = createFunctionTool({
name: 'format_temp',
description: 'Format temperature reading',
execute: ({ temp, unit }) => `${temp}°${unit.toUpperCase()}`,
parameters: [
{ name: 'temp', type: 'number', required: true },
{ name: 'unit', type: 'string', required: true }
]
});
// Compose tools with higher-order functions
const cachedWeatherTool = withCache(fetchWeatherTool, { ttl: 300000 }); // 5 min cache
const reliableWeatherTool = withRetry(cachedWeatherTool, { maxRetries: 3 });
// Chain tools together
const weatherReportTool = composeTool([
reliableWeatherTool,
formatTemperatureTool
], 'weather_report', 'Get formatted weather report');
import { compose, createValidator } from '@juspay-jaf/jaf';
// Base validators
const isPositive = createValidator<number>(
n => n > 0,
'Value must be positive'
);
const isInteger = createValidator<number>(
n => Number.isInteger(n),
'Value must be an integer'
);
const isInRange = (min: number, max: number) => createValidator<number>(
n => n >= min && n <= max,
`Value must be between ${min} and ${max}`
);
// Compose validators
const validateAge = compose(
isPositive,
isInteger,
isInRange(0, 150)
);
// Use in tool parameters
const ageTool = createFunctionTool({
name: 'process_age',
description: 'Process age data',
execute: ({ age }) => `Age ${age} is valid`,
parameters: [{
name: 'age',
type: 'number',
required: true,
validate: validateAge
}]
});
import { createAgent, withMiddleware, withFallback } from '@juspay-jaf/jaf';
// Base agents
const primaryAgent = createAgent({
name: 'primary',
model: 'gpt-4',
instruction: 'Primary processing agent',
tools: [calculatorTool]
});
const fallbackAgent = createAgent({
name: 'fallback',
model: 'gpt-3.5-turbo',
instruction: 'Fallback processing agent',
tools: [simpleMathTool]
});
// Compose with middleware
const loggingMiddleware = (agent) => ({
...agent,
execute: async (input) => {
console.log(`[${agent.name}] Processing:`, input);
const result = await agent.execute(input);
console.log(`[${agent.name}] Result:`, result);
return result;
}
});
const rateLimitMiddleware = (limit: number) => (agent) => {
let count = 0;
const resetTime = Date.now() + 60000;
return {
...agent,
execute: async (input) => {
if (Date.now() > resetTime) {
count = 0;
}
if (count >= limit) {
throw new Error('Rate limit exceeded');
}
count++;
return agent.execute(input);
}
};
};
// Compose everything
const productionAgent = compose(
withFallback(fallbackAgent),
withMiddleware(loggingMiddleware),
withMiddleware(rateLimitMiddleware(100))
)(primaryAgent);
import { composeMemoryProviders, createCacheLayer } from '@juspay-jaf/jaf';
// Layer memory providers for performance and reliability
const memoryProvider = composeMemoryProviders([
createCacheLayer({ maxSize: 100 }), // L1: In-memory cache
createRedisProvider({ ttl: 3600 }), // L2: Redis cache
createPostgresProvider({ table: 'chat' }) // L3: Persistent storage
]);
// The composed provider automatically:
// - Reads from the fastest available layer
// - Writes to all layers
// - Falls back on layer failure
import { createPathValidator, createPermissionValidator, composeValidations } from '@juspay-jaf/jaf';
// Create individual validators
const pathValidator = createPathValidator(['/shared', '/public']);
const permissionValidator = createPermissionValidator('admin', ctx => ctx);
// Compose them
const combinedValidator = composeValidations(pathValidator, permissionValidator);
// Apply to tools
const secureFileTool = withValidation(baseFileTool, combinedValidator);
import { createContentFilter, createRateLimiter } from '@juspay-jaf/jaf';
const config = {
// ... other config
initialInputGuardrails: [
createContentFilter(),
createRateLimiter(10, 60000, input => 'global')
],
finalOutputGuardrails: [
createContentFilter()
],
};
import { handoffTool } from '@juspay-jaf/jaf';
const triageAgent: Agent<Context, { agentName: string }> = {
name: 'TriageAgent',
instructions: () => 'Route requests to specialized agents',
tools: [handoffTool],
handoffs: ['MathTutor', 'FileManager'], // Allowed handoff targets
outputCodec: z.object({
agentName: z.enum(['MathTutor', 'FileManager'])
}),
};
import { ConsoleTraceCollector, FileTraceCollector } from '@juspay-jaf/jaf';
// Console logging
const consoleTracer = new ConsoleTraceCollector();
// File logging
const fileTracer = new FileTraceCollector('./traces.log');
// Composite tracing
const tracer = createCompositeTraceCollector(consoleTracer, fileTracer);
const config = {
// ... other config
onEvent: tracer.collect.bind(tracer),
};
import { JAFErrorHandler } from '@juspay-jaf/jaf';
if (result.outcome.status === 'error') {
const formattedError = JAFErrorHandler.format(result.outcome.error);
const isRetryable = JAFErrorHandler.isRetryable(result.outcome.error);
const severity = JAFErrorHandler.getSeverity(result.outcome.error);
console.error(`[${severity}] ${formattedError} (retryable: ${isRetryable})`);
}
import { makeLiteLLMProvider } from '@juspay-jaf/jaf';
// Connect to LiteLLM proxy for 100+ model support
const modelProvider = makeLiteLLMProvider(
'http://localhost:4000', // LiteLLM proxy URL
'your-api-key' // Optional API key
);
import { makeMCPClient, mcpToolToJAFTool } from '@juspay-jaf/jaf';
// Connect to MCP server
const mcpClient = await makeMCPClient('python', ['-m', 'mcp_server']);
// Get available tools
const mcpTools = await mcpClient.listTools();
// Convert to JAF tools with validation
const jafTools = mcpTools.map(tool =>
mcpToolToJAFTool(mcpClient, tool, myValidationPolicy)
);
JAF includes a built-in development server for testing agents locally via HTTP endpoints:
import { runServer, makeLiteLLMProvider, createInMemoryProvider } from '@juspay-jaf/jaf';
const myAgent = {
name: 'MyAgent',
instructions: 'You are a helpful assistant',
tools: [calculatorTool, greetingTool]
};
const modelProvider = makeLiteLLMProvider('http://localhost:4000');
const memoryProvider = createInMemoryProvider();
// Start server on port 3000
const server = await runServer(
[myAgent],
{ modelProvider },
{ port: 3000, defaultMemoryProvider: memoryProvider }
);
Server provides RESTful endpoints:
GET /health - Health checkGET /agents - List available agentsPOST /chat - General chat endpointPOST /agents/{name}/chat - Agent-specific endpointComprehensive documentation is available in the /docs folder:
Browse the full documentation online at https://xynehq.github.io/jaf/
The documentation site features:
# Install documentation dependencies
pip install -r requirements.txt
# Run local documentation server
mkdocs serve
# Visit http://127.0.0.1:8000
# Or use the convenience script
./docs/serve.sh
Explore the example applications to see the framework in action:
cd examples/server-demo
npm install
npm run dev
The server demo showcases:
cd examples/rag-demo
npm install
npm run dev
The RAG demo showcases:
npm test # Run tests
npm run lint # Lint code
npm run typecheck # Type checking
JAF - Building the future of functional AI agent systems 🚀
FAQs
Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools
The npm package @juspay-jaf/jaf receives a total of 503 weekly downloads. As such, @juspay-jaf/jaf popularity was classified as not popular.
We found that @juspay-jaf/jaf demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.