@memberjunction/ai-agents 5.23.0 → 5.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +104 -55
- package/dist/AgentRunner.js.map +1 -1
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +107 -47
- package/dist/base-agent.js.map +1 -1
- package/package.json +13 -13
package/dist/base-agent.js
CHANGED
|
@@ -832,13 +832,36 @@ export class BaseAgent {
|
|
|
832
832
|
this._activeProvider = params.provider || Metadata.Provider;
|
|
833
833
|
try {
|
|
834
834
|
this.logStatus(`🤖 Starting execution of agent '${params.agent.Name}'`, true, params);
|
|
835
|
-
//
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
835
|
+
// =====================================================================================
|
|
836
|
+
// LATENCY OPTIMIZATION (Opt #4): Parallelized initialization sequence.
|
|
837
|
+
//
|
|
838
|
+
// The agent initialization pipeline was originally fully sequential: each operation
|
|
839
|
+
// awaited before the next began, even when there were no data dependencies between
|
|
840
|
+
// them. This added ~150-200ms of unnecessary serial wait time.
|
|
841
|
+
//
|
|
842
|
+
// The restructured pipeline uses 4 phases:
|
|
843
|
+
//
|
|
844
|
+
// PRE-WORK (sync/fast): Parameter wrapping, markup conversion, payload init,
|
|
845
|
+
// cancellation check, payload validation (may exit early).
|
|
846
|
+
//
|
|
847
|
+
// PHASE 1 (parallel): Permission check + Engine init + AgentRun creation.
|
|
848
|
+
// These three are mutually independent. If permission fails, we mark the
|
|
849
|
+
// already-created AgentRun as failed and terminate — the wasted run record
|
|
850
|
+
// is a negligible cost vs. the ~150ms saved by not serializing these.
|
|
851
|
+
//
|
|
852
|
+
// PHASE 2 (parallel): Config load + Data preload + Context memory injection.
|
|
853
|
+
// All three depend on Phase 1 completing (engines loaded, agentRun exists)
|
|
854
|
+
// but NOT on each other. Config load reads from AIEngine's in-memory cache.
|
|
855
|
+
// Data preload fetches agent data sources. Context memory loads notes/examples
|
|
856
|
+
// and injects them into the conversation messages.
|
|
857
|
+
//
|
|
858
|
+
// PHASE 3 (sequential): Agent type initialization — must wait for config from
|
|
859
|
+
// Phase 2 because it needs the resolved agent type and prompt configuration.
|
|
860
|
+
//
|
|
861
|
+
// Original total init time: ~sum of all operations (~400-500ms)
|
|
862
|
+
// Optimized: ~max(Phase1) + max(Phase2) + Phase3 (~200-300ms)
|
|
863
|
+
// =====================================================================================
|
|
864
|
+
// --- PRE-WORK: Fast synchronous setup and early-exit checks ---
|
|
842
865
|
// Wrap the progress callback to capture all events
|
|
843
866
|
const wrappedParams = {
|
|
844
867
|
...params,
|
|
@@ -850,12 +873,20 @@ export class BaseAgent {
|
|
|
850
873
|
}
|
|
851
874
|
// Reset scratchpad for each new execution (ephemeral per run)
|
|
852
875
|
this._scratchpadManager.Clear();
|
|
876
|
+
// Initialize starting payload — must complete before AgentRun creation since the
|
|
877
|
+
// run record stores the starting payload snapshot.
|
|
853
878
|
await this.initializeStartingPayload(wrappedParams);
|
|
854
879
|
// Check for cancellation at start
|
|
855
880
|
if (params.cancellationToken?.aborted) {
|
|
856
881
|
this.logStatus(`⚠️ Agent '${params.agent.Name}' execution cancelled before start`, true, params);
|
|
857
882
|
return await this.createCancelledResult('Cancelled before execution started', params.contextUser);
|
|
858
883
|
}
|
|
884
|
+
// Handle starting payload validation if configured — may return early with a
|
|
885
|
+
// validation failure result, so we run this before launching expensive parallel work.
|
|
886
|
+
const startingValidationResult = await this.handleStartingPayloadValidation(wrappedParams);
|
|
887
|
+
if (startingValidationResult) {
|
|
888
|
+
return startingValidationResult;
|
|
889
|
+
}
|
|
859
890
|
// Report initialization progress
|
|
860
891
|
wrappedParams.onProgress?.({
|
|
861
892
|
step: 'initialization',
|
|
@@ -865,31 +896,44 @@ export class BaseAgent {
|
|
|
865
896
|
hierarchicalStep: this.buildHierarchicalStep(0, this._parentStepCounts)
|
|
866
897
|
}
|
|
867
898
|
});
|
|
868
|
-
//
|
|
869
|
-
|
|
870
|
-
//
|
|
899
|
+
// --- PHASE 1: Permission check + Engine init + AgentRun creation (parallel) ---
|
|
900
|
+
// These three operations have zero data dependencies on each other:
|
|
901
|
+
// - Permission check queries the AIAgentPermission entity
|
|
902
|
+
// - Engine init calls AIEngine.Instance.Config() and ActionEngineServer.Instance.Config()
|
|
903
|
+
// - AgentRun creation inserts a new AIAgentRun record
|
|
904
|
+
//
|
|
905
|
+
// If permission check fails, we mark the AgentRun as failed. This is acceptable:
|
|
906
|
+
// an orphaned "failed" run record is harmless and far cheaper than serializing these
|
|
907
|
+
// three operations (~150ms savings).
|
|
908
|
+
const [canRun] = await Promise.all([
|
|
909
|
+
AIAgentPermissionHelper.HasPermission(params.agent.ID, params.contextUser, 'run'),
|
|
910
|
+
this.initializeEngines(params.contextUser),
|
|
911
|
+
this.initializeAgentRun(wrappedParams)
|
|
912
|
+
]);
|
|
913
|
+
if (!canRun) {
|
|
914
|
+
// Permission denied — mark the already-created AgentRun as failed so it doesn't
|
|
915
|
+
// appear as a phantom "running" record in the UI.
|
|
916
|
+
const errorMessage = `User ${params.contextUser.Email} does not have permission to run agent '${params.agent.Name}' (ID: ${params.agent.ID})`;
|
|
917
|
+
this.logStatus(`🚫 ${errorMessage}`, false, params);
|
|
918
|
+
if (this._agentRun) {
|
|
919
|
+
this._agentRun.Status = 'Failed';
|
|
920
|
+
this._agentRun.ErrorMessage = errorMessage;
|
|
921
|
+
await this._agentRun.Save();
|
|
922
|
+
}
|
|
923
|
+
throw new Error(errorMessage);
|
|
924
|
+
}
|
|
925
|
+
// Reset per-run state (sync, instant — no parallelization needed)
|
|
871
926
|
this._validationRetryCount = 0;
|
|
872
927
|
this._generalValidationRetryCount = 0;
|
|
873
928
|
this._contextRecoveryAttempts = 0;
|
|
874
|
-
// Reset effective actions and dynamic limits for this run
|
|
875
929
|
this._effectiveActions = [];
|
|
876
930
|
this._dynamicActionLimits = {};
|
|
877
|
-
// Reset media outputs accumulator for this run
|
|
878
|
-
// (unified array now includes both promoted media and intercepted binary with refIds)
|
|
879
931
|
this._mediaOutputs = [];
|
|
880
|
-
// Store message lifecycle callback if provided
|
|
881
932
|
this._messageLifecycleCallback = params.onMessageLifecycle;
|
|
882
|
-
//
|
|
883
|
-
await this.initializeEngines(params.contextUser);
|
|
884
|
-
// Check for cancellation after initialization
|
|
933
|
+
// Check for cancellation after Phase 1
|
|
885
934
|
if (params.cancellationToken?.aborted) {
|
|
886
935
|
return await this.createCancelledResult('Cancelled during initialization', params.contextUser);
|
|
887
936
|
}
|
|
888
|
-
// Handle starting payload validation if configured
|
|
889
|
-
const startingValidationResult = await this.handleStartingPayloadValidation(wrappedParams);
|
|
890
|
-
if (startingValidationResult) {
|
|
891
|
-
return startingValidationResult;
|
|
892
|
-
}
|
|
893
937
|
// Report validation progress
|
|
894
938
|
wrappedParams.onProgress?.({
|
|
895
939
|
step: 'validation',
|
|
@@ -899,34 +943,26 @@ export class BaseAgent {
|
|
|
899
943
|
hierarchicalStep: this.buildHierarchicalStep(0, this._parentStepCounts)
|
|
900
944
|
}
|
|
901
945
|
});
|
|
902
|
-
//
|
|
946
|
+
// Validate agent — may return early with a failure result. Runs after engines are
|
|
947
|
+
// initialized (Phase 1) since validation may inspect AIEngine metadata.
|
|
903
948
|
const validationResult = await this.validateAgentWithTracking(params.agent, params.contextUser);
|
|
904
949
|
if (validationResult)
|
|
905
950
|
return validationResult;
|
|
906
|
-
//
|
|
951
|
+
// --- PHASE 2: Config load + Data preload + Context memory injection (parallel) ---
|
|
952
|
+
// All three depend on Phase 1 completing (engines initialized, agentRun exists) but
|
|
953
|
+
// have no dependencies on each other:
|
|
954
|
+
// - Config load reads agent type, prompts, and agent prompts from AIEngine's in-memory
|
|
955
|
+
// cache — typically < 5ms but async due to potential Config() refresh.
|
|
956
|
+
// - Data preload fetches agent data sources and creates a tracking step entity.
|
|
957
|
+
// - Context memory resolves scope configuration (pure computation), then loads notes
|
|
958
|
+
// and examples from the DB and injects them into conversation messages.
|
|
907
959
|
this.logStatus(`📋 Loading configuration for agent '${params.agent.Name}'`, true, params);
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
this.logError(`Failed to load agent configuration: ${config.errorMessage}`, {
|
|
911
|
-
agent: params.agent,
|
|
912
|
-
category: 'AgentConfiguration'
|
|
913
|
-
});
|
|
914
|
-
return await this.createFailureResult(config.errorMessage || 'Failed to load agent configuration', params.contextUser);
|
|
915
|
-
}
|
|
916
|
-
// Preload agent data sources unless disabled
|
|
917
|
-
await this.preloadAgentData(wrappedParams);
|
|
918
|
-
// now initialize the agent type which gets us the instance setup in our class plus also gets the agent type to initialize
|
|
919
|
-
// its state
|
|
920
|
-
await this.initializeAgentType(wrappedParams, config);
|
|
921
|
-
// Inject context memory (notes and examples) before execution
|
|
960
|
+
// Pre-compute scope configuration for context memory injection (pure computation,
|
|
961
|
+
// no I/O — safe to do before launching the parallel phase).
|
|
922
962
|
const userId = params.userId || params.contextUser?.ID;
|
|
923
963
|
const companyId = params.companyId;
|
|
924
|
-
|
|
925
|
-
const lastUserMessage = params.conversationMessages
|
|
926
|
-
.filter(m => m.role === 'user')
|
|
927
|
-
.pop();
|
|
964
|
+
const lastUserMessage = params.conversationMessages.filter(m => m.role === 'user').pop();
|
|
928
965
|
const inputText = lastUserMessage?.content || '';
|
|
929
|
-
// Parse agent-level scope config for note/example filtering
|
|
930
966
|
const scopeConfigJson = params.agent.ScopeConfig;
|
|
931
967
|
let scopeConfig = null;
|
|
932
968
|
if (scopeConfigJson) {
|
|
@@ -935,11 +971,9 @@ export class BaseAgent {
|
|
|
935
971
|
}
|
|
936
972
|
catch { /* ignore bad JSON */ }
|
|
937
973
|
}
|
|
938
|
-
// Resolve scope params from top-level params or data fallback (for GraphQL callers)
|
|
939
974
|
const primaryScopeEntityName = params.PrimaryScopeEntityName ?? params.data?.PrimaryScopeEntityName;
|
|
940
975
|
const primaryScopeRecordId = params.PrimaryScopeRecordID ?? params.data?.PrimaryScopeRecordID;
|
|
941
976
|
const secondaryScopes = params.SecondaryScopes ?? params.data?.SecondaryScopes;
|
|
942
|
-
// Resolve entity name to entity ID for scope filtering
|
|
943
977
|
let primaryScopeEntityId;
|
|
944
978
|
if (primaryScopeEntityName) {
|
|
945
979
|
const primaryEntity = this._metadata.Entities.find(e => e.Name === primaryScopeEntityName);
|
|
@@ -947,8 +981,22 @@ export class BaseAgent {
|
|
|
947
981
|
primaryScopeEntityId = primaryEntity.ID;
|
|
948
982
|
}
|
|
949
983
|
}
|
|
950
|
-
|
|
951
|
-
|
|
984
|
+
const [config] = await Promise.all([
|
|
985
|
+
this.loadAgentConfiguration(params.agent),
|
|
986
|
+
this.preloadAgentData(wrappedParams),
|
|
987
|
+
this.InjectContextMemory(typeof inputText === 'string' ? inputText : '', params.agent, userId, companyId, params.contextUser, wrappedParams.conversationMessages, primaryScopeEntityId, primaryScopeRecordId, secondaryScopes, scopeConfig)
|
|
988
|
+
]);
|
|
989
|
+
if (!config.success) {
|
|
990
|
+
this.logError(`Failed to load agent configuration: ${config.errorMessage}`, {
|
|
991
|
+
agent: params.agent,
|
|
992
|
+
category: 'AgentConfiguration'
|
|
993
|
+
});
|
|
994
|
+
return await this.createFailureResult(config.errorMessage || 'Failed to load agent configuration', params.contextUser);
|
|
995
|
+
}
|
|
996
|
+
// --- PHASE 3: Agent type initialization (sequential) ---
|
|
997
|
+
// Must wait for config from Phase 2 because it needs the resolved agent type and
|
|
998
|
+
// prompt configuration to initialize the type-specific state machine.
|
|
999
|
+
await this.initializeAgentType(wrappedParams, config);
|
|
952
1000
|
// Execute the agent's internal logic with wrapped parameters
|
|
953
1001
|
this.logStatus(`🚀 Executing agent '${params.agent.Name}' internal logic`, true, params);
|
|
954
1002
|
const executionResult = await this.executeAgentInternal(wrappedParams, config);
|
|
@@ -3618,6 +3666,18 @@ The context is now within limits. Please retry your request with the recovered c
|
|
|
3618
3666
|
lines.push('');
|
|
3619
3667
|
lines.push(`**User:** ${user.Name}${user.Roles?.length ? ` (Roles: ${user.Roles.join(', ')})` : ''}`);
|
|
3620
3668
|
}
|
|
3669
|
+
// Additional context reported by the active view/component
|
|
3670
|
+
const dashboardCtx = ctx['AdditionalContext'];
|
|
3671
|
+
if (dashboardCtx && Object.keys(dashboardCtx).length > 0) {
|
|
3672
|
+
lines.push('');
|
|
3673
|
+
lines.push('**Dashboard state:**');
|
|
3674
|
+
for (const [key, value] of Object.entries(dashboardCtx)) {
|
|
3675
|
+
if (value === null || value === undefined)
|
|
3676
|
+
continue;
|
|
3677
|
+
const displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
3678
|
+
lines.push(`- ${key}: ${displayValue}`);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3621
3681
|
return lines.join('\n');
|
|
3622
3682
|
}
|
|
3623
3683
|
/**
|