
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
@memberjunction/ai-agents
Advanced tools
This npm package provides a complete framework for building AI agents using the MemberJunction platform. Agents can execute prompts, invoke actions, and orchestrate complex workflows with comprehensive execution tracking.
The @memberjunction/ai-agents package enables developers to create sophisticated AI agents that can:
The core execution engine that all agents use. Provides functionality for:
Abstract class that defines reusable agent behavior patterns:
Concrete implementation of BaseAgentType that:
Deterministic workflow agent type that:
Orchestrator that provides multiple execution modes:
Advanced payload access control for hierarchical agent execution:
npm install @memberjunction/ai-agents
import { AgentRunner } from '@memberjunction/ai-agents';
import { UserInfo } from '@memberjunction/core';
// Using AgentRunner (recommended) which uses `ClassFactory` to pick the highest priority sub-class of BaseAgent that matches your Agent (and falls back to just using `BaseAgent` if there's no custom sub-class)
const runner = new AgentRunner();
const result = await runner.RunAgent({
agent: agentEntity, // AIAgentEntity from database
conversationMessages: messages,
contextUser: user
});
// Direct instantiation when you want to pick the exact class that gets run
const agent = new YourAgentClass();
const result = await agent.Execute({
agent: agentEntity,
conversationMessages: messages,
contextUser: user
});
// Using run chaining to maintain context across multiple runs
const followUpResult = await runner.RunAgent({
agent: agentEntity,
conversationMessages: newMessages,
contextUser: user,
lastRunId: result.agentRun.ID,
autoPopulateLastRunPayload: true // Automatically use previous run's final payload
});
The RunAgentInConversation method provides a complete workflow for executing agents within a conversation context, automatically handling conversation creation, artifact generation, and linking:
import { AgentRunner } from '@memberjunction/ai-agents';
import { UserInfo } from '@memberjunction/core';
const runner = new AgentRunner();
// Execute agent with automatic conversation and artifact management
const result = await runner.RunAgentInConversation({
agent: agentEntity,
conversationMessages: messages,
contextUser: user
}, {
// Optional: Use existing conversation
conversationId: 'existing-conversation-id',
// Optional: Use existing conversation detail (skips creation)
conversationDetailId: 'existing-detail-id',
// Required if conversationDetailId not provided
userMessage: 'Analyze the sales data for Q4',
// Optional: Control artifact creation (default: true)
createArtifacts: true,
// Optional: Source artifact for versioning (continuity/refinement)
sourceArtifactId: 'base-artifact-id',
// Optional: Custom conversation name
conversationName: 'Q4 Sales Analysis'
});
// Result includes everything you need
console.log('Agent result:', result.agentResult);
console.log('Conversation ID:', result.conversationId);
console.log('Detail ID:', result.conversationDetailId);
if (result.artifactInfo) {
console.log('Artifact created:', result.artifactInfo.artifactId);
console.log('Version:', result.artifactInfo.versionNumber);
}
The method provides a complete workflow:
Conversation Management
conversationId providedconversationDetailId providedConversation Detail Creation
__mj_CreatedAtconversationDetailId already providedAgent Execution
Artifact Processing (if createArtifacts !== false)
sourceArtifactId if provided (explicit continuity)ArtifactCreationMode configurationThe method implements smart artifact versioning:
// Priority 1: Explicit source artifact (agent continuity/refinement)
{
sourceArtifactId: 'artifact-to-refine'
// Creates version 2, 3, 4, etc. of the specified artifact
}
// Priority 2: Previous artifact on this conversation detail (fallback)
// Automatically finds last artifact linked to the conversation detail
// Creates next version of that artifact
// Priority 3: No previous artifact
// Creates entirely new artifact with version 1
The method honors agent-level settings:
ArtifactCreationMode === 'Never': Skips artifact creation entirelyArtifactCreationMode === 'System Only': Creates artifact with Visibility='System Only'DefaultArtifactTypeID: Uses agent's preferred artifact type (defaults to JSON type)GraphQL Resolvers - Simplify agent execution endpoints:
// Before: Manually manage conversations, artifacts, notifications
// After: One method call handles everything
const result = await runner.RunAgentInConversation({...}, {
conversationDetailId: args.conversationDetailId,
createArtifacts: args.createArtifacts,
sourceArtifactId: args.sourceArtifactId
});
Interactive Chat Interfaces - Maintain conversation context:
// First message - creates conversation
const firstResult = await runner.RunAgentInConversation({...}, {
userMessage: 'Analyze sales data',
createArtifacts: true
});
// Follow-up message - uses existing conversation
const followUp = await runner.RunAgentInConversation({...}, {
conversationId: firstResult.conversationId,
userMessage: 'Show me the trends',
createArtifacts: true
});
Agent Refinement Workflows - Iterate on artifacts:
// Initial generation
const initial = await runner.RunAgentInConversation({...}, {
userMessage: 'Create a report',
createArtifacts: true
});
// Refinement - creates version 2 of same artifact
const refined = await runner.RunAgentInConversation({...}, {
userMessage: 'Make it more concise',
createArtifacts: true,
sourceArtifactId: initial.artifactInfo.artifactId
});
RunAgentInConversation uses these public helper methods (available for custom workflows):
// Get maximum version number for an artifact
const maxVersion = await runner.GetMaxVersionForArtifact(artifactId, user);
// Find previous artifact for a conversation detail
const previousArtifact = await runner.FindPreviousArtifactForMessage(detailId, user);
// Process agent artifacts manually
const artifactInfo = await runner.ProcessAgentArtifacts(
agentResult,
conversationDetailId,
sourceArtifactId,
user
);
Both Flow and Loop agents support native ForEach and While iterations for efficient batch processing and retry logic.
📘 Complete Guide: Guide to Iterative Operations in Agents
Flow Agent - ForEach Example:
// Create a ForEach step that sends email to each customer
const forEachStep = await md.GetEntityObject<AIAgentStepEntity>('MJ: AI Agent Steps');
forEachStep.StepType = 'ForEach';
forEachStep.LoopBodyType = 'Action';
forEachStep.ActionID = sendEmailActionId;
forEachStep.Configuration = JSON.stringify({
type: 'ForEach',
collectionPath: 'payload.customers',
itemVariable: 'customer',
maxIterations: 500
});
forEachStep.ActionInputMapping = JSON.stringify({
to: 'customer.email',
subject: 'Welcome!'
});
Loop Agent - ForEach Example:
{
"taskComplete": false,
"message": "Processing all documents",
"nextStep": {
"type": "ForEach",
"forEach": {
"collectionPath": "payload.documents",
"action": {
"name": "Analyze Document",
"params": { "path": "item.path" }
}
}
}
}
Benefits:
See the complete guide for While loops, nested iterations, and advanced patterns.
Agents are configured through MemberJunction entities:
AIAgentType: Defines the behavior pattern and system prompt
DriverClass: The TypeScript class implementing the behaviorSystemPromptID: Base prompt providing foundational behaviorAIAgent: Specific agent instance
AgentTypeID: Links to the agent typeAIPrompt: Reusable prompt templates
AIAgentPrompt: Associates prompts with agents
ExecutionOrder: Determines prompt execution sequenceAIAgentStep: Defines workflow steps for Flow agents
StepType: Action, Sub-Agent, or PromptStartingStep: Boolean flag for initial stepsTimeoutSeconds: Step execution timeoutActionOutputMapping: JSON mapping for action resultsAIAgentStepPath: Connects workflow steps
Condition: Boolean expression for path evaluationPriority: Determines path selection orderimport { BaseAgentType, RegisterClass, BaseAgentNextStep } from '@memberjunction/ai-agents';
import { AIPromptRunResult } from '@memberjunction/ai-prompts';
@RegisterClass(BaseAgentType, "MyCustomAgentType")
export class MyCustomAgentType extends BaseAgentType {
async DetermineNextStep(promptResult: AIPromptRunResult): Promise<BaseAgentNextStep> {
// Parse the prompt result
const response = JSON.parse(promptResult.FullResult);
// Determine next action based on response
if (response.taskComplete) {
return { type: 'stop', reason: 'Task completed successfully' };
} else if (response.action) {
return {
type: 'action',
actionName: response.action.name,
actionParams: response.action.params
};
} else {
return { type: 'continue' };
}
}
}
When AI prompts use validation syntax (like ?, *, :type, etc.), the AI might inadvertently include these in its JSON response keys. Agent types that parse embedded JSON need to handle this cleaning:
import { JSONValidator } from '@memberjunction/global';
@RegisterClass(BaseAgentType, "StructuredResponseAgent")
export class StructuredResponseAgent extends BaseAgentType {
async DetermineNextStep(promptResult: AIPromptRunResult): Promise<BaseAgentNextStep> {
// For responses with embedded JSON strings
const outerResponse = promptResult.result as any;
// If the response contains an embedded JSON string
if (outerResponse.response && typeof outerResponse.response === 'string') {
// Parse the embedded JSON
const innerData = JSON.parse(outerResponse.response);
// Clean validation syntax that AI might have included
const validator = new JSONValidator();
const cleanedData = validator.cleanValidationSyntax<any>(innerData);
// Now work with cleaned data
if (cleanedData.analysisComplete) {
return { type: 'stop', reason: 'Analysis completed' };
}
}
return { type: 'continue' };
}
}
Important Notes:
cleanValidationSyntax method preserves values while removing validation syntax from keysExample scenario:
// AI Prompt response (top-level is cleaned automatically)
{
"status": "success",
"response": "{\"analysisComplete?\":true,\"recommendations:[3+]\":[\"A\",\"B\",\"C\"]}"
}
// After agent type cleans the embedded JSON
{
"analysisComplete": true,
"recommendations": ["A", "B", "C"]
}
Initialization:
Configuration Loading:
Execution Loop:
Result Tracking:
Get the AgentRun ID immediately after creation for real-time monitoring:
const params: ExecuteAgentParams = {
agent: myAgent,
conversationMessages: messages,
contextUser: currentUser,
// Callback fired immediately after AgentRun record is saved
onAgentRunCreated: async (agentRunId) => {
console.log(`Agent run started: ${agentRunId}`);
// Use cases:
// - Link to parent records (e.g., AIAgentRunStep.TargetLogID for sub-agents)
// - Send to monitoring systems
// - Update UI with tracking info
// - Start real-time log streaming
}
};
const result = await runner.RunAgent(params);
The callback is invoked:
Agents can declaratively preload reference data without requiring custom application code or action calls. Data sources are configured through the AIAgentDataSource entity and automatically loaded before agent execution.
Data preloading solves the common problem of agents needing access to reference data (like entity lists, configuration values, or initial state) that doesn't change during execution. Instead of:
Agents can now specify data sources that are automatically loaded and injected into the appropriate destination (data, context, or payload).
1. Data Destination - For Nunjucks templates in prompts (visible to LLMs)
// Configuration
{
"Name": "ALL_ENTITIES",
"SourceType": "RunView",
"EntityName": "Entities",
"OrderBy": "Name ASC",
"DestinationType": "Data",
"DestinationPath": null // Uses "ALL_ENTITIES" at root level
}
// Result in agent prompt:
// params.data.ALL_ENTITIES = [{ Name: "Users", ... }, { Name: "Entities", ... }]
// Prompt can use Nunjucks:
// You have access to {{ALL_ENTITIES.length}} entities:
// {% for entity in ALL_ENTITIES %}
// - {{entity.Name}}: {{entity.Description}}
// {% endfor %}
2. Context Destination - For actions only (NOT visible to LLMs)
// Configuration
{
"Name": "ORG_SETTINGS",
"SourceType": "RunView",
"EntityName": "Organization Settings",
"ExtraFilter": "OrgID='${context.organizationId}'",
"DestinationType": "Context",
"DestinationPath": "organization.settings"
}
// Result:
// params.context.organization.settings = { apiEndpoint: "...", features: [...] }
// Actions can access context, but prompts/LLMs cannot
// This keeps API keys and sensitive configuration away from LLMs
3. Payload Destination - For agent state initialization
// Configuration
{
"Name": "CustomerOrders",
"SourceType": "RunQuery",
"QueryName": "Recent Orders by Customer",
"Parameters": JSON.stringify({ customerId: "{{context.customerId}}" }),
"DestinationType": "Payload",
"DestinationPath": "analysis.orders.recent"
}
// Result:
// params.payload.analysis.orders.recent = [{ OrderID: "123", ... }]
// Agent starts with rich initial state without caller manually loading it
RunView Data Sources - Query entities with filters
{
"Name": "ACTIVE_MODELS",
"SourceType": "RunView",
"EntityName": "AI Models",
"ExtraFilter": "IsActive=1 AND Vendor='OpenAI'",
"OrderBy": "Priority DESC",
"FieldsToRetrieve": JSON.stringify(["ID", "Name", "Vendor", "MaxInputTokens"]),
"ResultType": "simple", // or "entity_object"
"MaxRows": 100,
"DestinationType": "Data"
}
RunQuery Data Sources - Execute stored queries
{
"Name": "MONTHLY_STATS",
"SourceType": "RunQuery",
"QueryName": "Monthly Analytics",
"CategoryPath": "/Reports/Analytics",
"Parameters": JSON.stringify({
month: "{{context.currentMonth}}",
year: "{{context.currentYear}}"
}),
"DestinationType": "Payload",
"DestinationPath": "stats.monthly"
}
The DestinationPath field supports nested paths using dot notation:
// Simple root-level
{
"Name": "ENTITIES",
"DestinationPath": null // Uses "ENTITIES" at root
}
// Result: data.ENTITIES
// Nested paths
{
"Name": "ModelList",
"DestinationPath": "config.ai.models"
}
// Result: data.config.ai.models
// Deep nesting
{
"Name": "CustomerData",
"DestinationPath": "analysis.customer.profile.orders"
}
// Result: payload.analysis.customer.profile.orders
Data sources support three caching strategies:
1. None - No caching (default)
{
"CachePolicy": "None"
// Data is loaded fresh every time
}
2. PerRun - Cache for duration of a single agent run
{
"CachePolicy": "PerRun"
// Multiple data sources with same AgentID+Name share cached data within one run
// Cache is cleared when agent run completes
}
3. PerAgent - Global cache with TTL
{
"CachePolicy": "PerAgent",
"CacheTimeoutSeconds": 3600 // 1 hour
// Cached across all runs for this agent until TTL expires
// Good for rarely-changing reference data like entity lists
}
Disable data preloading for specific executions:
const result = await runner.RunAgent({
agent: myAgent,
conversationMessages: messages,
contextUser: user,
disableDataPreloading: true // Skip automatic data preloading
});
Caller precedence: Caller-provided data always takes precedence over preloaded data:
const result = await runner.RunAgent({
agent: myAgent,
conversationMessages: messages,
contextUser: user,
data: {
CUSTOM_ENTITIES: myEntities // Overrides preloaded CUSTOM_ENTITIES
}
});
Database Research Agent - Preload entity metadata
// Data source 1: All entities for reference
{
"AgentID": "database-research-agent-id",
"Name": "ALL_ENTITIES",
"SourceType": "RunView",
"EntityName": "Entities",
"OrderBy": "Name ASC",
"FieldsToRetrieve": JSON.stringify(["ID", "Name", "SchemaName", "Description", "BaseView"]),
"DestinationType": "Data",
"ExecutionOrder": 1,
"Status": "Active",
"CachePolicy": "PerAgent",
"CacheTimeoutSeconds": 3600
}
// Data source 2: Schema information
{
"AgentID": "database-research-agent-id",
"Name": "SCHEMA_INFO",
"SourceType": "RunView",
"EntityName": "Entity Fields",
"DestinationType": "Data",
"DestinationPath": "schema.fields",
"ExecutionOrder": 2,
"Status": "Active",
"CachePolicy": "PerAgent",
"CacheTimeoutSeconds": 3600
}
Customer Service Agent - Preload customer context
// Preload customer data into payload
{
"AgentID": "customer-service-agent-id",
"Name": "CUSTOMER_PROFILE",
"SourceType": "RunView",
"EntityName": "Customers",
"ExtraFilter": "ID='{{context.customerId}}'",
"DestinationType": "Payload",
"DestinationPath": "customer.profile",
"Status": "Active",
"CachePolicy": "PerRun"
}
// Preload recent orders
{
"AgentID": "customer-service-agent-id",
"Name": "RECENT_ORDERS",
"SourceType": "RunQuery",
"QueryName": "Recent Orders by Customer",
"Parameters": JSON.stringify({ customerId: "{{context.customerId}}", days: 30 }),
"DestinationType": "Payload",
"DestinationPath": "customer.orders",
"Status": "Active",
"CachePolicy": "PerRun"
}
// Preload organization settings (for actions)
{
"AgentID": "customer-service-agent-id",
"Name": "ORG_CONFIG",
"SourceType": "RunView",
"EntityName": "Organization Settings",
"ExtraFilter": "OrgID='{{context.organizationId}}'",
"DestinationType": "Context",
"DestinationPath": "organization.config",
"Status": "Active",
"CachePolicy": "PerAgent",
"CacheTimeoutSeconds": 1800
}
The AIAgentDataSource table includes:
Unique Constraint: AgentID + Name + DestinationType + DestinationPath
The framework now supports narrowing the payload that sub-agents work with through the PayloadScope field:
// Configure an agent to work with a specific part of the payload
const subAgent = {
Name: 'RequirementsAnalyzer',
PayloadScope: '/functionalRequirements', // Only sees this part of parent payload
PayloadSelfWritePaths: ['analysis', 'recommendations'] // Paths within the scope
};
// When parent payload is:
{
"functionalRequirements": {
"features": ["A", "B", "C"],
"constraints": {...}
},
"technicalSpecs": {...},
"timeline": {...}
}
// Sub-agent only sees:
{
"features": ["A", "B", "C"],
"constraints": {...}
}
// Sub-agent changes are merged back under the scope path
Benefits:
Agents can validate their input payload before execution begins to ensure data quality and prevent errors:
// Configure validation in AIAgent entity
{
StartingPayloadValidation: JSON.stringify({
"customerId": "string:!empty",
"orderItems": "array:[1+]",
"shippingAddress": {
"street": "string:!empty",
"city": "string:!empty",
"zipCode": "string:[5]"
},
"priority": "string?:enum:normal,high,urgent" // Optional with enum values
}),
StartingPayloadValidationMode: "Fail" // or "Warn" (default: "Fail")
}
Input validation features:
Fail: Reject invalid input immediately (default)Warn: Log warning but proceed with executionAgents can validate their final output before marking execution as successful:
// Configure validation in AIAgent entity
{
FinalPayloadValidation: JSON.stringify({
"analysis": {
"summary": "string:!empty",
"score": "number:[0-100]",
"recommendations": "array:[3+]"
},
"metadata": {
"processedAt": "string",
"version": "string?" // Optional field
}
}),
FinalPayloadValidationMode: "Retry", // or "Fail" or "Warn"
FinalPayloadValidationMaxRetries: 3
}
Validation features:
Retry: Re-execute with validation feedback (up to max retries)Fail: Immediately fail the runWarn: Log warning but allow successNew fields provide comprehensive limits to prevent runaway agent execution:
// Configure guardrails in AIAgent entity
{
MaxCostPerRun: 10.00, // $10 maximum
MaxTokensPerRun: 100000, // 100k tokens total
MaxIterationsPerRun: 50, // 50 prompt iterations
MaxTimePerRun: 300 // 5 minutes
}
// The framework monitors these in real-time and terminates if exceeded
// Termination reason is logged in AIAgentRun.ErrorMessage
Guardrail features:
The framework supports linking multiple agent runs together to maintain context across interactions:
// Execute an agent with run chaining
const result = await agent.Execute({
agent: agentEntity,
conversationMessages: messages,
contextUser: user,
lastRunId: previousRunId, // Links to previous run
autoPopulateLastRunPayload: true // Auto-loads previous payload
});
// The framework will:
// 1. Load the FinalPayload from the previous run
// 2. Set it as StartingPayload for the new run
// 3. Use it as the initial payload if none provided
// 4. Validate against circular references in the chain
Key features:
The framework includes sophisticated payload management with automatic change detection:
// Payload changes are automatically analyzed
const changeResult = payloadManager.applyAgentChangeRequest(
originalPayload,
changeRequest,
{
analyzeChanges: true, // Detect suspicious changes
generateDiff: true, // Create audit trail
agentName: 'MyAgent'
}
);
// Suspicious changes are flagged:
// - Content truncation (>70% reduction)
// - Non-empty key removal
// - Type changes (object→primitive)
// - Pattern anomalies (placeholder replacement)
Sub-agent Payload Access Control:
// In AIAgent entity configuration:
{
PayloadDownstreamPaths: ["customer.id", "order.*"], // What sub-agent can read
PayloadUpstreamPaths: ["analysis.*", "recommendations"] // What sub-agent can write
}
Operation-Level Payload Control: The framework supports fine-grained control over which operations (add, update, delete) are allowed on specific payload paths:
// Basic syntax - all operations allowed (backward compatible)
PayloadUpstreamPaths: ["analysis.*", "recommendations"]
// Operation-specific syntax using colon notation
PayloadUpstreamPaths: [
"analysis.*:add,update", // Can add or update, but not delete
"recommendations:add", // Can only add new recommendations
"summary:update", // Can only update existing summary
"temp.*:delete", // Can only delete temporary data
"metadata.tags:add,delete" // Can add/remove tags but not modify existing
]
// For agent's own payload access (PayloadSelfWritePaths)
PayloadSelfWritePaths: [
"workspace.*", // Full access to workspace
"results:add", // Can only add results, not modify
"status:update" // Can only update status field
]
Operation types:
add - Create new properties or array elementsupdate - Modify existing valuesdelete - Remove properties or array elementsWhen operations are restricted, the framework will:
// System prompt provides base behavior
// Agent prompts execute as children with shared context
const result = await agent.ExecutePrompt({
systemPrompt: agentType.SystemPrompt,
agentPrompt: currentPrompt,
messages: conversationContext
});
Agents automatically manage conversation context:
The framework provides sophisticated message lifecycle management to prevent context bloat from large action results:
Per-Action Configuration (in AIAgentAction table):
ResultExpirationTurns: Number of turns before message expires (e.g., 2)ResultExpirationMode: 'None' | 'Remove' | 'Compact'CompactMode: 'First N Chars' | 'AI Summary'CompactLength: Character limit for 'First N Chars' modeCompactPromptID: Custom AI prompt for 'AI Summary' modeHow It Works:
// Configure a Google Search action to compact results after 2 turns
await agentAction.Save({
ResultExpirationTurns: 2,
ResultExpirationMode: 'Compact',
CompactMode: 'First N Chars',
CompactLength: 500
});
// Turn 1: Action returns 10,000 char search results
// Turn 2: Results still in conversation (turn 1, limit 2)
// Turn 3: Results still in conversation (turn 2, limit 2)
// Turn 4: Results compacted to 500 chars (turn 3 > limit 2)
// Original content preserved in metadata for expansion
Compaction Modes:
First N Chars: Fast truncation with annotation
First 500 chars of result...
[Compacted: showing first 500 of 10000 characters. Agent can request expansion if needed.]
AI Summary: Intelligent LLM-based summarization
[AI Summary of 10000 chars. Agent can request full expansion if needed.]
Search found 47 results for "MemberJunction". Top results include...
Message Expansion: Agents can restore compacted messages when needed:
// In agent's JSON response
{
"taskComplete": false,
"nextStep": {
"type": "Retry",
"messageIndex": 5, // Index of compacted message
"reason": "Need full search results to answer user's question about item #47"
}
}
Runtime Override: Test different expiration strategies without modifying database:
const result = await runner.RunAgent({
agent: myAgent,
conversationMessages: messages,
contextUser: user,
messageExpirationOverride: {
expirationTurns: 1,
expirationMode: 'Compact',
compactMode: 'First N Chars',
compactLength: 200,
preserveOriginalContent: true
}
});
Lifecycle Monitoring: Track message compaction for debugging and token savings analysis:
const result = await runner.RunAgent({
agent: myAgent,
conversationMessages: messages,
contextUser: user,
onMessageLifecycle: (event) => {
console.log(`[Turn ${event.turn}] ${event.type}: ${event.reason}`);
if (event.tokensSaved) {
console.log(` Tokens saved: ${event.tokensSaved}`);
}
}
});
// Output:
// [Turn 3] message-compacted: Compacted using First N Chars (saved 2375 tokens)
// [Turn 5] message-removed: Removed due to expiration
Prompt Lookup Hierarchy: For AI Summary mode, prompts are resolved in this order:
messageExpirationOverride.compactPromptId)AIAgentAction.CompactPromptID)Action.DefaultCompactPromptID)Benefits:
When a prompt execution fails due to context length overflow (even after model failover), BaseAgent provides one-time automatic recovery instead of immediately terminating. This gives the agent an opportunity to adapt its approach.
How It Works:
Example Recovery Message:
⚠️ CONTEXT OVERFLOW RECOVERY ⚠️
The previous step returned a result that exceeded the context window (147,532 characters truncated).
Here is a PARTIAL result from the previous action:
---
[First 10 items from JSON array...]
... (487 more items truncated due to context length)
---
❗ THE ABOVE IS INCOMPLETE - the full result was too large for the context window.
RECOMMENDED ACTIONS:
1. Use a different action with more specific filters to get smaller result sets
2. Request data in batches or pages instead of all at once
3. Ask the user to clarify scope to narrow the query
4. If you need the full data, acknowledge the limitation and ask the user how to proceed
Please choose an alternative approach to complete your task.
Benefits:
This feature is particularly useful when agents call actions that can return very large datasets (e.g., "Get Entity List" without filters).
// In agent type's DetermineNextStep
return {
type: 'action',
actionName: 'SendEmail',
actionParams: {
to: 'user@example.com',
subject: 'Analysis Complete',
body: analysisResult
}
};
// Agents can invoke other agents recursively
return {
type: 'sub_agent',
agentName: 'DataValidationAgent',
messages: [
{ role: 'user', content: `Validate this data: ${JSON.stringify(data)}` }
]
};
The framework includes a ConversationMessageResolver utility that enables flexible conversation message referencing in action input mappings and sub-agent configurations. This is particularly useful for passing conversation context to knowledge base assistants, chatbots, or analysis agents.
In Flow Agent Step Configuration (ActionInputMapping):
// Pass full conversation history to an action
{
"ActionInputMapping": {
"ConversationMessages": "conversation.all"
}
}
In Loop Agent Response:
{
"taskComplete": false,
"nextStep": {
"type": "Actions",
"actions": [{
"name": "Betty", // Knowledge base assistant
"params": {
"ConversationMessages": "conversation.all"
}
}]
}
}
The ConversationMessageResolver supports powerful pattern-based message selection:
1. All Messages:
"ConversationMessages": "conversation.all"
// Returns entire conversation history
2. Role-Based Selection:
// Last N user messages
"ConversationMessages": "conversation.user.last[5]"
// Last N assistant messages
"ConversationMessages": "conversation.assistant.last[3]"
// Last N system messages
"ConversationMessages": "conversation.system.last[1]"
3. All Messages of a Role:
// All user messages
"ConversationMessages": "conversation.user.all"
// All assistant messages
"ConversationMessages": "conversation.assistant.all"
4. Single Last Message by Role:
// Just the last user message
"ConversationMessages": "conversation.user.last"
// Just the last assistant message
"ConversationMessages": "conversation.assistant.last"
Knowledge Base Assistants - Pass full conversation history for context-aware responses:
// In Knowledge Base Research Agent step
{
"StepType": "Action",
"ActionID": "betty-action-id",
"ActionInputMapping": {
"ConversationMessages": "conversation.all" // Betty sees full context
}
}
Sentiment Analysis - Analyze just user messages:
{
"ActionInputMapping": {
"messagesToAnalyze": "conversation.user.last[10]",
"includeSentiment": true
}
}
Context Summarization - Summarize recent conversation:
{
"ActionInputMapping": {
"recentMessages": "conversation.all", // or "conversation.last[20]" if supported
"summarizeAs": "bullet_points"
}
}
Follow-up Question Generation - Based on assistant responses:
{
"ActionInputMapping": {
"previousResponses": "conversation.assistant.last[3]",
"generateFollowUps": true
}
}
Sub-agents automatically receive the parent's conversation context, but you can control which messages are passed:
In Loop Agent (via agent prompt instructions):
// The agent's system prompt can instruct:
"When invoking sub-agents, you can specify which conversation messages to pass using the ConversationMessages parameter in your action input mappings."
In Flow Agent (via SubAgentConfiguration):
// Configure sub-agent relationships with conversation context
{
"AgentID": "parent-agent-id",
"SubAgentID": "knowledge-base-research-agent-id",
"SubAgentOutputMapping": { "*": "knowledgeBaseResearch" },
"SubAgentContextPaths": ["*"] // Full context by default
}
The resolver operates during the parameter mapping phase:
conversation. prefixed strings in action/sub-agent parametersExample Resolution:
// Input mapping configuration
{
"ConversationMessages": "conversation.user.last[3]"
}
// Conversation history
[
{ role: 'system', content: 'You are a helpful assistant' },
{ role: 'user', content: 'What is MemberJunction?' },
{ role: 'assistant', content: 'MemberJunction is...' },
{ role: 'user', content: 'How do agents work?' },
{ role: 'assistant', content: 'Agents work by...' },
{ role: 'user', content: 'Can you give an example?' }
]
// Resolved parameter value (last 3 user messages)
[
{ role: 'user', content: 'What is MemberJunction?' },
{ role: 'user', content: 'How do agents work?' },
{ role: 'user', content: 'Can you give an example?' }
]
A prime example of this feature is the Betty action for knowledge base queries:
// Betty action accepts ConversationMessages parameter
{
"name": "Betty",
"params": {
"ConversationMessages": "conversation.all" // Full conversation context
}
}
// Betty uses the conversation history to provide context-aware responses
// and can reference earlier questions/answers in its knowledge base queries
This enables knowledge base agents to maintain conversation context across multiple queries, improving response relevance and follow-up question handling.
The agent framework includes a comprehensive ACL-based permissions system that controls who can view, run, edit, and delete agents.
The permissions system uses hierarchical permissions with the following levels:
The system uses an "open by default" approach to minimize administrative overhead:
Why this approach?
Every agent has an OwnerUserID field:
Permission records are stored in the AIAgentPermission table with these fields:
Important: Each record must have either UserID OR RoleID set, but not both.
When checking if a user can perform an operation:
The framework provides helper methods for checking permissions:
import { AIEngineBase } from '@memberjunction/ai-engine-base';
import { AIAgentPermissionHelper } from '@memberjunction/ai-engine-base';
// Check specific permission
const canRun = await AIEngineBase.Instance.CanUserRunAgent(agentId, user);
const canEdit = await AIEngineBase.Instance.CanUserEditAgent(agentId, user);
// Get all effective permissions
const permissions = await AIEngineBase.Instance.GetUserAgentPermissions(agentId, user);
console.log(permissions);
// {
// canView: true,
// canRun: true,
// canEdit: false,
// canDelete: false,
// isOwner: false
// }
// Get all agents user can access with specific permission
const runnableAgents = await AIEngineBase.Instance.GetAccessibleAgents(user, 'run');
// Using the helper directly
const hasPermission = await AIAgentPermissionHelper.HasPermission(agentId, user, 'run');
The BaseAgent class automatically enforces run permissions:
// BaseAgent.Execute() checks permissions before running
const result = await agent.Execute({
agent: agentEntity,
conversationMessages: messages,
contextUser: user
});
// If user lacks run permission, execution fails with:
// Error: "User {email} does not have permission to run agent '{name}'"
The AI Agent form includes a "Permissions" button that opens a dialog for managing permissions:
Permissions are cached in the AIEngineBase metadata system for performance:
// Clear cache after modifying permissions
AIEngineBase.Instance.ClearAgentPermissionsCache();
// Refresh cache for specific agent
await AIEngineBase.Instance.RefreshAgentPermissionsCache(agentId, user);
Scenario 1: Public Agent
Scenario 2: Department Agent
Scenario 3: Restricted Agent
Scenario 4: Shared Ownership
Key entities used by the agent framework:
OwnerUserID: User who owns the agent (defaults to creator, grants full permissions)PayloadDownstreamPaths: JSON array of paths sub-agents can readPayloadUpstreamPaths: JSON array of paths sub-agents can writePayloadScope: Path to narrow payload for sub-agents (e.g., "/functionalRequirements")StartingPayloadValidation: JSON validation schema for input validationStartingPayloadValidationMode: How to handle input validation failures (Fail/Warn)FinalPayloadValidation: JSON validation schema for success validationFinalPayloadValidationMode: How to handle validation failures (Retry/Fail/Warn)FinalPayloadValidationMaxRetries: Maximum retry attempts for validation (default: 3)MaxCostPerRun: Cost limit per agent runMaxTokensPerRun: Token limit per agent runMaxIterationsPerRun: Iteration limit per agent runMaxTimePerRun: Time limit in seconds per agent runAgentID: The agent being controlledUserID: Direct user permission (exclusive with RoleID)RoleID: Role-based permission (exclusive with UserID)CanView: View agent configurationCanRun: Execute the agentCanEdit: Modify agent configurationCanDelete: Remove the agentComments: Optional permission descriptionLastRunID: Links to previous run in a chain (for run chaining)StartingPayload: Initial payload for the runTotalPromptIterations: Count of prompt executions in the runPayloadAtStart: JSON snapshot of payload before stepPayloadAtEnd: JSON snapshot of payload after stepOutputData: Includes payloadChangeResult with analysisFinalPayloadValidationResult: Validation outcome (Pass/Retry/Fail/Warn)FinalPayloadValidationMessages: Validation error messages// Agent type configured with LoopAgentType driver
// System prompt defines JSON response format
// Agent prompts execute tasks iteratively
const result = await runner.RunAgent({
agent: loopAgent,
conversationMessages: [
{ role: 'user', content: 'Analyze these sales figures and create a report' }
],
contextUser: user
});
// Configure an agent with specific operation permissions
const analysisAgent = {
Name: 'DataAnalysisAgent',
PayloadSelfWritePaths: JSON.stringify([
"workspace.*", // Full control over workspace
"analysis.results:add", // Can only add new results
"analysis.status:update", // Can only update status
"temp.*:add,delete" // Can add/delete temp data, but not modify
])
};
// Configure a sub-agent with restricted write access
const validationAgent = {
Name: 'ValidationAgent',
PayloadDownstreamPaths: JSON.stringify([
"data.*", // Can read all data
"analysis.results" // Can read analysis results
]),
PayloadUpstreamPaths: JSON.stringify([
"data.validated:update", // Can only update validation flag
"errors:add", // Can only add errors, not modify
"warnings:add,delete" // Can add/remove warnings
])
};
// When the sub-agent tries unauthorized operations:
// - Attempt to delete data.records → Blocked (no delete permission)
// - Attempt to update errors → Blocked (only add permission)
// - Add new warning → Allowed
// - Update data.validated → Allowed
@RegisterClass(BaseAgentType, "DecisionTreeAgent")
export class DecisionTreeAgent extends BaseAgentType {
async DetermineNextStep(result: AIPromptRunResult): Promise<BaseAgentNextStep> {
const decision = JSON.parse(result.FullResult);
switch(decision.branch) {
case 'needs_data':
return { type: 'action', actionName: 'FetchData', actionParams: decision.params };
case 'analyze':
return { type: 'sub_agent', agentName: 'AnalysisAgent', messages: decision.context };
case 'complete':
return { type: 'stop', reason: decision.summary };
default:
return { type: 'continue' };
}
}
}
Flow agents execute deterministic, graph-based workflows where the execution path is determined by boolean conditions evaluated against the payload and step results. Unlike Loop agents that rely on LLM decision-making at each step, Flow agents follow predefined paths through a directed graph.
Flow agents are ideal for:
Steps are the nodes in your workflow graph. Each step represents an action to perform:
// Three types of steps:
{
Name: 'ValidateInput',
StepType: 'Action', // Execute a MJ Action
ActionID: 'validation-action-id',
StartingStep: true, // Marks this as an entry point
Sequence: 0, // For parallel starting steps
Status: 'Active', // Active, Disabled, or Pending
TimeoutSeconds: 30 // Optional timeout
}
{
Name: 'AnalyzeData',
StepType: 'Prompt', // Execute an AI prompt
PromptID: 'analysis-prompt-id',
Description: 'Analyze data quality and completeness'
}
{
Name: 'ProcessWithSubAgent',
StepType: 'Sub-Agent', // Invoke another agent
SubAgentID: 'processing-agent-id'
}
Paths are the edges connecting your workflow nodes. They determine the flow:
{
OriginStepID: 'step-a-id',
DestinationStepID: 'step-b-id',
Condition: 'payload.amount > 1000 && payload.approved === true',
Priority: 10 // Higher priority paths evaluated first
}
// Path without condition (always valid)
{
OriginStepID: 'step-a-id',
DestinationStepID: 'default-step-id',
Condition: null, // No condition = always valid
Priority: 0 // Lower priority = fallback
}
Array Append Syntax - When mapping outputs that can occur multiple times, use the [] suffix to append values to an array instead of replacing them:
// SubAgentOutputMapping for agents that can be called multiple times
{
"*": "codeAnalysis[]" // Append each sub-agent result to array
}
// ActionOutputMapping for actions that run in loops
{
"result": "findings[]", // Append to array
"score": "scores[]", // Each iteration adds to array
"*": "rawResults.allData[]" // Wildcard append
}
// Without [] suffix (default behavior - replace)
{
"*": "latestResult" // Each call REPLACES the value
}
// Array append features:
// - Auto-initializes array if it doesn't exist
// - Validates target is an array before appending
// - Prevents data loss from multiple sub-agent/action calls
// - Works with both simple and nested payload paths
When to Use Array Append:
Example Use Case:
// Research Agent with Codesmith sub-agent for code-based analytics
// Each time Codesmith is called, its output is appended to codeAnalysis[]
// First call to Codesmith
{
"name": "Sales Trend Analysis",
"code": "...",
"output": { trend: "increasing", rate: 0.15 }
}
// Second call to Codesmith
{
"name": "Customer Segmentation",
"code": "...",
"output": { segments: [...] }
}
// Final payload.codeAnalysis array contains BOTH results:
[
{ name: "Sales Trend Analysis", output: {...} },
{ name: "Customer Segmentation", output: {...} }
]
Action Input Mapping (ActionInputMapping) - Maps payload values to action parameters:
// In AIAgentStep.ActionInputMapping
{
"customerId": "payload.customer.id", // Map from payload
"orderDate": "static:2024-01-01", // Static value
"includeDetails": true, // Boolean literal
"maxResults": 100, // Numeric literal
"filters": { // Nested object
"status": "payload.filters.orderStatus",
"region": "static:US-WEST"
},
"itemIds": "payload.order.items" // Can map arrays
}
// Supports nested resolution
{
"searchParams": {
"query": "payload.searchTerm",
"filters": {
"category": "payload.category",
"tags": "payload.selectedTags"
},
"options": {
"maxResults": 50,
"includeMetadata": true
}
}
}
Action Output Mapping (ActionOutputMapping) - Maps action results back to payload or special fields:
// In AIAgentStep.ActionOutputMapping
{
"userId": "payload.customer.id", // Map specific output param
"orderTotal": "payload.order.total", // Nested path in payload
"metadata": "payload.action.lastResult", // Arbitrary nesting
"*": "payload.rawResults.fullData", // Wildcard = entire result
"responseText": "$message", // Special field - user message
"analysisDetails": "$reasoning", // Special field - reasoning
"confidenceScore": "$confidence" // Special field - confidence
}
// Case-insensitive output parameter matching
// If action returns { UserId: "123" }, it matches "userId" in mapping
Special Fields (Flow Agents Only):
Use the $ prefix to map action outputs to special response fields instead of the payload:
$message: Maps to the user-facing message in the final Success step$reasoning: Optional reasoning/explanation shown with the response$confidence: Optional confidence score (number) for the responseExample - Betty Knowledge Base Agent:
// Betty action returns: { BettyResponse: "The answer is...", BettyReferences: [...] }
{
"BettyResponse": "$message", // Shows directly to user
"BettyReferences": "references" // Stored in payload
}
// When flow completes, user sees Betty's response as the message
// No LLM processing needed - deterministic, single-step flow
Special Field Benefits:
message in payloadWhen a Prompt step executes, its JSON response is deep merged into the payload:
// Before prompt execution
payload = {
decision: {
status: "pending",
reviewerId: "user-123"
},
metadata: { startTime: "..." }
};
// Prompt returns
promptResponse = {
decision: {
approved: true,
confidence: 0.95
}
};
// After deep merge (preserves existing keys!)
payload = {
decision: {
approved: true, // NEW from prompt
confidence: 0.95, // NEW from prompt
status: "pending", // PRESERVED from before
reviewerId: "user-123" // PRESERVED from before
},
metadata: { startTime: "..." } // PRESERVED
};
Why Deep Merge?
Special Prompt Response Handling:
// If prompt response contains Chat step request
{
"nextStep": { "type": "Chat" },
"message": "I need more information from the user",
"taskComplete": false
}
// OR
{
"taskComplete": true,
"message": "Here's the final result..."
}
// Flow agent returns Chat step to bubble message to user
// This allows prompts within flows to communicate with users
// Database configuration for a complete approval workflow
// 1. Define the workflow steps
const steps = [
{
Name: 'ValidateRequest',
StepType: 'Action',
ActionID: validateActionId,
StartingStep: true,
Sequence: 0,
ActionInputMapping: JSON.stringify({
"requestData": "payload.request",
"validationRules": "payload.rules"
}),
ActionOutputMapping: JSON.stringify({
"isValid": "payload.validation.isValid",
"errors": "payload.validation.errors"
})
},
{
Name: 'CheckAmount',
StepType: 'Prompt',
PromptID: amountCheckPromptId,
Description: 'AI analyzes amount and risk factors'
// Prompt returns: { risk: "low"|"medium"|"high", reasoning: "..." }
// Deep merged into payload.risk and payload.reasoning
},
{
Name: 'AutoApprove',
StepType: 'Action',
ActionID: approveActionId,
ActionInputMapping: JSON.stringify({
"requestId": "payload.request.id",
"approvedBy": "static:SYSTEM_AUTO"
}),
ActionOutputMapping: JSON.stringify({
"approvalId": "payload.approval.id",
"timestamp": "payload.approval.timestamp"
})
},
{
Name: 'ManagerReview',
StepType: 'Sub-Agent',
SubAgentID: managerReviewAgentId
// Sub-agent payload inherits and can modify parent payload
},
{
Name: 'NotifyUser',
StepType: 'Action',
ActionID: notificationActionId,
ActionInputMapping: JSON.stringify({
"userId": "payload.request.userId",
"message": "payload.approval.notificationMessage",
"channel": "static:email"
})
}
];
// 2. Define the workflow paths
const paths = [
// From validation
{
OriginStepID: validateStepId,
DestinationStepID: checkAmountStepId,
Condition: 'payload.validation.isValid === true',
Priority: 10
},
{
OriginStepID: validateStepId,
DestinationStepID: notifyUserStepId,
Condition: 'payload.validation.isValid === false',
Priority: 10
},
// From AI risk assessment
{
OriginStepID: checkAmountStepId,
DestinationStepID: autoApproveStepId,
Condition: 'payload.risk === "low" && payload.request.amount <= 1000',
Priority: 10
},
{
OriginStepID: checkAmountStepId,
DestinationStepID: managerReviewStepId,
Condition: 'payload.risk === "medium" || payload.risk === "high"',
Priority: 10
},
// From manager review
{
OriginStepID: managerReviewStepId,
DestinationStepID: autoApproveStepId,
Condition: 'payload.managerDecision.approved === true',
Priority: 10
},
{
OriginStepID: managerReviewStepId,
DestinationStepID: notifyUserStepId,
Condition: 'payload.managerDecision.approved === false',
Priority: 5
},
// Final notification after approval
{
OriginStepID: autoApproveStepId,
DestinationStepID: notifyUserStepId,
Condition: null, // Always execute
Priority: 0
}
];
// 3. Execute the flow agent
const result = await runner.RunAgent({
agent: flowAgentEntity,
conversationMessages: messages,
contextUser: user,
payload: {
request: {
id: "req-123",
userId: "user-456",
amount: 5000,
description: "Equipment purchase"
},
rules: {
maxAutoApprove: 1000,
requiresManagerReview: true
}
}
});
Flow agents use the SafeExpressionEvaluator to securely evaluate path conditions without arbitrary code execution:
// Supported operations in conditions:
// - Comparisons: ==, ===, !=, !==, <, >, <=, >=
// - Logical: &&, ||, !
// - Property access: payload.user.role, stepResult.score
// - Safe methods: .includes(), .length, .some(), .every()
// - Type checking: typeof
// Example conditions:
"payload.status == 'approved' && payload.priority > 5"
"stepResult.items.some(item => item.price > 100)"
"payload.user.roles.includes('admin') || payload.override === true"
Automatically map action results to the payload:
// In AIAgentStep.ActionOutputMapping
{
"userId": "payload.customer.id", // Map specific output
"orderTotal": "payload.order.total", // Nested path mapping
"*": "payload.actionResults.lastResult" // Wildcard for entire result
}
The framework maintains flow execution state in __flowContext:
// Automatically tracked in payload.__flowContext
{
agentId: "flow-agent-id",
currentStepId: "current-step-id",
completedStepIds: ["step1", "step2"],
stepResults: {
"step1": { success: true, data: {...} },
"step2": { approved: false }
},
executionPath: ["step1", "step2", "step3"]
}
Flow agents can incorporate AI decision points:
// Prompt step expects response format:
{
"nextStepName?": "StepToExecute",
"reasoning?": "Why this decision was made",
"confidence?": 0.95,
"terminate?": false,
"message?": "Decision explanation"
}
For detailed architecture information, see agent-architecture.md.
Contributions are welcome! Please see the main MemberJunction contributing guide.
The AI Agents framework supports flexible API key management through integration with the AI Prompts system, including the new environment-based configuration features.
AI Agents benefit from MemberJunction's environment-based configuration system, allowing different API keys and settings per environment:
// Agents automatically use the correct configuration based on NODE_ENV
// Development -> AIConfigSet(Name='development') -> Different API keys
// Production -> AIConfigSet(Name='production') -> Production API keys
// This is especially useful for:
// - Agent testing with development API keys
// - Production agents with higher rate limits
// - Environment-specific agent behaviors
You can provide API keys at agent execution time for multi-tenant scenarios:
import { AgentRunner, ExecuteAgentParams } from '@memberjunction/ai-agents';
import { AIAPIKey } from '@memberjunction/ai';
const runner = new AgentRunner();
// Execute agent with specific API keys
const result = await runner.RunAgent({
agent: agentEntity,
conversationMessages: messages,
contextUser: user,
apiKeys: [
{ driverClass: 'OpenAILLM', apiKey: 'sk-user-specific-key' },
{ driverClass: 'AnthropicLLM', apiKey: 'sk-ant-department-key' }
]
});
// API keys are automatically propagated to:
// - All prompt executions by the agent
// - Sub-agent executions
// - Context compression operations
When agents execute, API keys are resolved in this priority order:
// Example: Different agent configurations per environment
// Development environment
const devAgentConfig = {
ConfigSet: { Name: 'development', Priority: 100 },
Configurations: [
{ ConfigKey: 'OPENAI_LLM_APIKEY', ConfigValue: 'sk-dev-...', Encrypted: true },
{ ConfigKey: 'MAX_AGENT_ITERATIONS', ConfigValue: '10', Encrypted: false },
{ ConfigKey: 'AGENT_DEBUG_MODE', ConfigValue: 'true', Encrypted: false }
]
};
// Production environment
const prodAgentConfig = {
ConfigSet: { Name: 'production', Priority: 100 },
Configurations: [
{ ConfigKey: 'OPENAI_LLM_APIKEY', ConfigValue: 'sk-prod-...', Encrypted: true },
{ ConfigKey: 'MAX_AGENT_ITERATIONS', ConfigValue: '50', Encrypted: false },
{ ConfigKey: 'AGENT_DEBUG_MODE', ConfigValue: 'false', Encrypted: false }
]
};
// Agents can access these configurations through the AI engine
Runtime API keys and environment-based configuration are particularly useful for agent architectures:
// Custom agent that uses environment-based configuration
@RegisterClass(BaseAgent, "ConfigAwareAgent")
export class ConfigAwareAgent extends BaseAgent {
protected async getConfiguration(key: string): Promise<string | null> {
// The framework automatically loads configurations based on NODE_ENV
const envName = process.env.NODE_ENV || 'production';
// Query AIConfiguration for the current environment
const config = await this.loadConfigValue(envName, key);
return config;
}
protected async setupExecution(): Promise<void> {
// Load agent-specific configurations
const maxIterations = await this.getConfiguration('MAX_AGENT_ITERATIONS');
const debugMode = await this.getConfiguration('AGENT_DEBUG_MODE');
// Apply configurations to agent behavior
this.maxIterations = parseInt(maxIterations || '50');
this.debugMode = debugMode === 'true';
}
}
For detailed information about API key configuration and management, see the AI Prompts API Keys documentation.
Agents fully support the AI Configuration system for environment-specific model selection. When you execute an agent with a configurationId, that configuration is automatically propagated to:
const result = await runner.RunAgent({
agent: myAgent,
conversationMessages: messages,
contextUser: user,
configurationId: 'dev-config-id', // Optional - propagates to all prompts
});
For comprehensive details about how AI Configurations work, including model selection logic and fallback behavior, see the AI Configuration System documentation.
Agents support sophisticated effort level management that controls how much reasoning effort AI models apply to each prompt execution. The effort level uses a 1-100 integer scale where higher values request more thorough analysis.
The effort level is resolved using hierarchical precedence:
ExecuteAgentParams.effortLevel) - Highest priorityAIAgent.DefaultPromptEffortLevel) - Medium priorityAIPrompt.EffortLevel) - Lower priority// Execute agent with high effort level for all prompts
const result = await runner.RunAgent({
agent: myAnalysisAgent,
conversationMessages: messages,
contextUser: user,
effortLevel: 85 // High effort - applies to all prompts in execution
});
// Execute with medium effort level
const result = await runner.RunAgent({
agent: myQuickAgent,
conversationMessages: messages,
contextUser: user,
effortLevel: 30 // Low effort - for quick responses
});
Sub-agents automatically inherit the effort level from their parent unless explicitly overridden:
// Parent agent runs with effort level 70
const parentResult = await runner.RunAgent({
agent: parentAgent,
effortLevel: 70, // Inherited by all sub-agents
// ...
});
// All sub-agents spawned during execution will use effort level 70
// unless the sub-agent has its own DefaultPromptEffortLevel setting
You can configure default effort levels at the agent level:
AIAgent.DefaultPromptEffortLevel: Sets the default effort level for all prompts executed by this agentDifferent AI providers handle effort levels differently:
For detailed effort level documentation, see the AI Core Plus documentation.
This package is part of the MemberJunction project. See the LICENSE file for details.
FAQs
MemberJunction: AI Agent Execution and Management
The npm package @memberjunction/ai-agents receives a total of 449 weekly downloads. As such, @memberjunction/ai-agents popularity was classified as not popular.
We found that @memberjunction/ai-agents demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 6 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
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.