@memberjunction/server 5.22.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.
Files changed (45) hide show
  1. package/README.md +35 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +11 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/generated/generated.d.ts +610 -4
  6. package/dist/generated/generated.d.ts.map +1 -1
  7. package/dist/generated/generated.js +17333 -13889
  8. package/dist/generated/generated.js.map +1 -1
  9. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  10. package/dist/generic/RunViewResolver.js.map +1 -1
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +7 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/resolvers/AutotagPipelineResolver.d.ts +30 -0
  16. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -0
  17. package/dist/resolvers/AutotagPipelineResolver.js +231 -0
  18. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -0
  19. package/dist/resolvers/ClientToolRequestResolver.d.ts +43 -0
  20. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -0
  21. package/dist/resolvers/ClientToolRequestResolver.js +161 -0
  22. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -0
  23. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +29 -0
  24. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -0
  25. package/dist/resolvers/FetchEntityVectorsResolver.js +222 -0
  26. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -0
  27. package/dist/resolvers/RunAIAgentResolver.d.ts +21 -0
  28. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  29. package/dist/resolvers/RunAIAgentResolver.js +75 -33
  30. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  31. package/dist/resolvers/SearchKnowledgeResolver.d.ts +42 -1
  32. package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
  33. package/dist/resolvers/SearchKnowledgeResolver.js +239 -13
  34. package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
  35. package/package.json +63 -63
  36. package/src/__tests__/search-knowledge-tags.test.ts +415 -0
  37. package/src/config.ts +11 -0
  38. package/src/generated/generated.ts +2373 -7
  39. package/src/generic/RunViewResolver.ts +1 -0
  40. package/src/index.ts +10 -0
  41. package/src/resolvers/AutotagPipelineResolver.ts +235 -0
  42. package/src/resolvers/ClientToolRequestResolver.ts +128 -0
  43. package/src/resolvers/FetchEntityVectorsResolver.ts +238 -0
  44. package/src/resolvers/RunAIAgentResolver.ts +97 -56
  45. package/src/resolvers/SearchKnowledgeResolver.ts +270 -13
@@ -0,0 +1,222 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Resolver, Query, Arg, Ctx, ObjectType, Field, Float, Int } from 'type-graphql';
14
+ import { LogError, LogStatus, RunView } from '@memberjunction/core';
15
+ import { ResolverBase } from '../generic/ResolverBase.js';
16
+ import { GetAIAPIKey } from '@memberjunction/ai';
17
+ import { VectorDBBase } from '@memberjunction/ai-vectordb';
18
+ import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
19
+ /* ───── GraphQL types ───── */
20
+ let EntityVectorItem = class EntityVectorItem {
21
+ };
22
+ __decorate([
23
+ Field(),
24
+ __metadata("design:type", String)
25
+ ], EntityVectorItem.prototype, "ID", void 0);
26
+ __decorate([
27
+ Field(() => [Float]),
28
+ __metadata("design:type", Array)
29
+ ], EntityVectorItem.prototype, "Values", void 0);
30
+ __decorate([
31
+ Field(() => String),
32
+ __metadata("design:type", String)
33
+ ], EntityVectorItem.prototype, "Metadata", void 0);
34
+ EntityVectorItem = __decorate([
35
+ ObjectType()
36
+ ], EntityVectorItem);
37
+ export { EntityVectorItem };
38
+ let FetchEntityVectorsResult = class FetchEntityVectorsResult {
39
+ };
40
+ __decorate([
41
+ Field(),
42
+ __metadata("design:type", Boolean)
43
+ ], FetchEntityVectorsResult.prototype, "Success", void 0);
44
+ __decorate([
45
+ Field(() => [EntityVectorItem]),
46
+ __metadata("design:type", Array)
47
+ ], FetchEntityVectorsResult.prototype, "Results", void 0);
48
+ __decorate([
49
+ Field(),
50
+ __metadata("design:type", Number)
51
+ ], FetchEntityVectorsResult.prototype, "TotalCount", void 0);
52
+ __decorate([
53
+ Field(),
54
+ __metadata("design:type", Number)
55
+ ], FetchEntityVectorsResult.prototype, "ElapsedMs", void 0);
56
+ __decorate([
57
+ Field({ nullable: true }),
58
+ __metadata("design:type", String)
59
+ ], FetchEntityVectorsResult.prototype, "ErrorMessage", void 0);
60
+ FetchEntityVectorsResult = __decorate([
61
+ ObjectType()
62
+ ], FetchEntityVectorsResult);
63
+ export { FetchEntityVectorsResult };
64
+ /* ───── Resolver ───── */
65
+ let FetchEntityVectorsResolver = class FetchEntityVectorsResolver extends ResolverBase {
66
+ async FetchEntityVectors(entityDocumentID, maxRecords, filter, { userPayload } = {}) {
67
+ const startTime = Date.now();
68
+ try {
69
+ const currentUser = this.GetUserFromPayload(userPayload);
70
+ if (!currentUser) {
71
+ return this.errorResult('Unable to determine current user', startTime);
72
+ }
73
+ const limit = maxRecords ?? 1000;
74
+ // Step 1: Load the EntityDocument
75
+ const entityDoc = await this.loadEntityDocument(entityDocumentID, currentUser);
76
+ if (!entityDoc) {
77
+ return this.errorResult(`EntityDocument not found: ${entityDocumentID}`, startTime);
78
+ }
79
+ // Step 2: Resolve the VectorIndex
80
+ const vectorIndex = await this.resolveVectorIndex(entityDoc, currentUser);
81
+ if (!vectorIndex) {
82
+ return this.errorResult(`Could not resolve VectorIndex for EntityDocument "${entityDoc.Name}"`, startTime);
83
+ }
84
+ // Step 3: Create VectorDB provider instance
85
+ const vectorDBInstance = await this.createVectorDBInstance(vectorIndex, currentUser);
86
+ if (!vectorDBInstance) {
87
+ return this.errorResult(`Could not create VectorDB provider for index "${vectorIndex.Name}"`, startTime);
88
+ }
89
+ // Step 4: Query with zero vector + Entity metadata filter.
90
+ // Pinecone's list API doesn't support metadata filtering, but query does.
91
+ // A zero vector returns results in arbitrary order (similarity is meaningless),
92
+ // but the metadata filter ensures we only get vectors for this entity.
93
+ const entityName = entityDoc.Entity;
94
+ const dimensions = vectorIndex.Dimensions || 1536; // fall back to common embedding size
95
+ // Use a tiny uniform vector instead of zero — cosine similarity is undefined
96
+ // for a zero vector (division by zero), causing Pinecone to return 0 matches.
97
+ // A uniform vector has equal similarity to all vectors, giving us an unbiased
98
+ // listing that respects the metadata filter.
99
+ const uniformVector = new Array(dimensions).fill(1.0 / Math.sqrt(dimensions));
100
+ const metadataFilter = { Entity: { $eq: entityName } };
101
+ const queryResponse = await vectorDBInstance.QueryIndex({
102
+ id: vectorIndex.Name, // index name (stripped before Pinecone query)
103
+ vector: uniformVector,
104
+ topK: limit,
105
+ includeMetadata: true,
106
+ includeValues: true,
107
+ filter: metadataFilter,
108
+ });
109
+ if (!queryResponse.success || !queryResponse.data) {
110
+ return this.errorResult(`Vector query failed for entity "${entityName}" in index "${vectorIndex.Name}"`, startTime);
111
+ }
112
+ // Step 5: Convert query matches to result format
113
+ const matches = queryResponse.data.matches ?? [];
114
+ const results = matches.map(match => ({
115
+ ID: match.id,
116
+ Values: match.values ?? [],
117
+ Metadata: JSON.stringify(match.metadata ?? {}),
118
+ }));
119
+ LogStatus(`FetchEntityVectors: Queried ${results.length} vectors for entity "${entityName}" in ${Date.now() - startTime}ms`);
120
+ return {
121
+ Success: true,
122
+ Results: results,
123
+ TotalCount: results.length,
124
+ ElapsedMs: Date.now() - startTime,
125
+ };
126
+ }
127
+ catch (error) {
128
+ const msg = error instanceof Error ? error.message : String(error);
129
+ LogError(`FetchEntityVectors query failed: ${msg}`);
130
+ return this.errorResult(msg, startTime);
131
+ }
132
+ }
133
+ /** Load EntityDocument by ID */
134
+ async loadEntityDocument(entityDocumentID, contextUser) {
135
+ const rv = new RunView();
136
+ const result = await rv.RunView({
137
+ EntityName: 'MJ: Entity Documents',
138
+ ExtraFilter: `ID='${entityDocumentID}'`,
139
+ ResultType: 'entity_object',
140
+ }, contextUser);
141
+ if (!result.Success || result.Results.length === 0) {
142
+ return null;
143
+ }
144
+ return result.Results[0];
145
+ }
146
+ /**
147
+ * Resolve the VectorIndex for an EntityDocument.
148
+ * Prefers the explicit VectorIndexID on the EntityDocument; falls back to
149
+ * finding a VectorIndex by matching VectorDatabaseID + EmbeddingModelID.
150
+ */
151
+ async resolveVectorIndex(entityDoc, contextUser) {
152
+ const rv = new RunView();
153
+ // If the EntityDocument has an explicit VectorIndexID, use it directly
154
+ if (entityDoc.VectorIndexID) {
155
+ const result = await rv.RunView({
156
+ EntityName: 'MJ: Vector Indexes',
157
+ ExtraFilter: `ID='${entityDoc.VectorIndexID}'`,
158
+ ResultType: 'entity_object',
159
+ }, contextUser);
160
+ if (result.Success && result.Results.length > 0) {
161
+ return result.Results[0];
162
+ }
163
+ LogError(`FetchEntityVectors: VectorIndex ${entityDoc.VectorIndexID} referenced by EntityDocument not found`);
164
+ }
165
+ // Fallback: find a VectorIndex by VectorDatabaseID + matching AIModelID as EmbeddingModelID
166
+ const indexResult = await rv.RunView({
167
+ EntityName: 'MJ: Vector Indexes',
168
+ ExtraFilter: `VectorDatabaseID='${entityDoc.VectorDatabaseID}'`,
169
+ ResultType: 'entity_object',
170
+ }, contextUser);
171
+ if (!indexResult.Success || indexResult.Results.length === 0) {
172
+ return null;
173
+ }
174
+ // Match on EmbeddingModelID = EntityDocument.AIModelID
175
+ const match = indexResult.Results.find(idx => UUIDsEqual(idx.EmbeddingModelID, entityDoc.AIModelID));
176
+ return match ?? indexResult.Results[0];
177
+ }
178
+ /** Create a VectorDBBase provider instance for a given VectorIndex */
179
+ async createVectorDBInstance(vectorIndex, contextUser) {
180
+ const rv = new RunView();
181
+ const dbResult = await rv.RunView({
182
+ EntityName: 'MJ: Vector Databases',
183
+ ExtraFilter: `ID='${vectorIndex.VectorDatabaseID}'`,
184
+ ResultType: 'entity_object',
185
+ }, contextUser);
186
+ if (!dbResult.Success || dbResult.Results.length === 0) {
187
+ LogError(`FetchEntityVectors: VectorDatabase not found for index "${vectorIndex.Name}"`);
188
+ return null;
189
+ }
190
+ const vectorDB = dbResult.Results[0];
191
+ const apiKey = GetAIAPIKey(vectorDB.ClassKey);
192
+ const instance = MJGlobal.Instance.ClassFactory.CreateInstance(VectorDBBase, vectorDB.ClassKey, apiKey);
193
+ if (!instance) {
194
+ LogError(`FetchEntityVectors: Failed to create VectorDB instance for ClassKey "${vectorDB.ClassKey}"`);
195
+ }
196
+ return instance;
197
+ }
198
+ errorResult(message, startTime) {
199
+ return {
200
+ Success: false,
201
+ Results: [],
202
+ TotalCount: 0,
203
+ ElapsedMs: Date.now() - startTime,
204
+ ErrorMessage: message,
205
+ };
206
+ }
207
+ };
208
+ __decorate([
209
+ Query(() => FetchEntityVectorsResult),
210
+ __param(0, Arg('entityDocumentID')),
211
+ __param(1, Arg('maxRecords', () => Int, { nullable: true })),
212
+ __param(2, Arg('filter', { nullable: true })),
213
+ __param(3, Ctx()),
214
+ __metadata("design:type", Function),
215
+ __metadata("design:paramtypes", [String, Number, String, Object]),
216
+ __metadata("design:returntype", Promise)
217
+ ], FetchEntityVectorsResolver.prototype, "FetchEntityVectors", null);
218
+ FetchEntityVectorsResolver = __decorate([
219
+ Resolver()
220
+ ], FetchEntityVectorsResolver);
221
+ export { FetchEntityVectorsResolver };
222
+ //# sourceMappingURL=FetchEntityVectorsResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FetchEntityVectorsResolver.js","sourceRoot":"","sources":["../../src/resolvers/FetchEntityVectorsResolver.ts"],"names":[],"mappings":"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"}
@@ -102,6 +102,13 @@ export declare class RunAIAgentResolver extends ResolverBase {
102
102
  * Create a user notification for agent completion with artifact
103
103
  * Notification includes navigation link back to the conversation
104
104
  */
105
+ /**
106
+ * LATENCY OPTIMIZATION (Opt #2): Now accepts conversationId directly instead of
107
+ * conversationDetailId. Previously this method loaded a ConversationDetail entity
108
+ * from the DB solely to extract its ConversationID field for building a URL — a
109
+ * redundant ~50ms DB round-trip since the caller already resolved conversationId
110
+ * when loading conversation history.
111
+ */
105
112
  private createCompletionNotification;
106
113
  /**
107
114
  * When a continuation run completes (lastRunId was provided), sync the corresponding
@@ -146,6 +153,20 @@ export declare class RunAIAgentResolver extends ResolverBase {
146
153
  /**
147
154
  * Load conversation history with attachments from database.
148
155
  * Builds ChatMessage[] with multimodal content blocks for attachments.
156
+ *
157
+ * LATENCY OPTIMIZATIONS (plans/agent-latency-optimization.md — Opts #3 and #8):
158
+ *
159
+ * Opt #3: This method now accepts conversationId directly instead of conversationDetailId.
160
+ * Previously it loaded a ConversationDetail entity object just to extract its ConversationID
161
+ * field — a redundant DB round-trip (~40ms) since the caller already has this information.
162
+ * The caller (RunAIAgentFromConversationDetail) now loads the ConversationDetail once and
163
+ * passes conversationId down.
164
+ *
165
+ * Opt #8: Switched from ResultType 'entity_object' to 'simple' with explicit Fields.
166
+ * The history query only needs ID, Role, and Message from each ConversationDetail record.
167
+ * Using 'entity_object' created full BaseEntity instances with getters/setters, dirty tracking,
168
+ * and validation — none of which are needed for read-only history assembly. The 'simple' result
169
+ * type returns plain JS objects, reducing per-record overhead (~30ms total savings).
149
170
  */
150
171
  private loadConversationHistoryWithAttachments;
151
172
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"RunAIAgentResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/RunAIAgentResolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RunAIAgentResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/RunAIAgentResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkE,YAAY,EAAmD,MAAM,cAAc,CAAC;AAC7J,OAAO,EAAE,UAAU,EAAe,MAAM,aAAa,CAAC;AAOtD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAQ1D,qBACa,gBAAgB;IAEzB,OAAO,EAAE,OAAO,CAAC;IAGjB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,qBACa,sBAAsB;IAE/B,WAAW,EAAE,MAAM,CAAC;IAGpB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,OAAO,EAAE,MAAM,CAAC;IAGhB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qBACa,qBAAqB;IAE9B,OAAO,EAAE,MAAM,CAAC;IAGhB,SAAS,EAAE,OAAO,CAAC;IAGnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBACa,yBAAyB;IAElC,MAAM,EAAE,MAAM,CAAC;IAGf,QAAQ,EAAE,MAAM,CAAC;IAGjB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,SAAS,EAAE,IAAI,CAAC;IAGhB,OAAO,CAAC,EAAE,IAAI,CAAC;IAGf,MAAM,EAAE,MAAM,CAAC;IAGf,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBACa,kBAAkB;IAE3B,WAAW,EAAE,MAAM,CAAC;IAGpB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBACa,2BAA2B;IAEpC,SAAS,EAAE,MAAM,CAAC;IAGlB,UAAU,EAAE,MAAM,CAAC;IAGnB,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,UAAU,CAAC;IAG/D,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAGlC,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAGlC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAGnC,SAAS,EAAE,IAAI,CAAC;IAGhB,QAAQ,CAAC,EAAE,GAAG,CAAC;IAGf,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACjC;AAMD,qBACa,kBAAmB,SAAQ,YAAY;IAChD;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;YACW,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6C9B,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,sBAAsB;IAY9B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAmC/B;;;;OAIG;YACW,cAAc;IAuL5B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA+C1B;;;OAGG;IAEG,UAAU,CACI,OAAO,EAAE,MAAM,EACxB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,UAAU,EACxC,YAAY,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACzB,MAAM,EAAE,YAAY,EACG,IAAI,CAAC,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,EACX,YAAY,CAAC,EAAE,MAAM,EACxB,SAAS,CAAC,EAAE,MAAM,EACD,0BAA0B,CAAC,EAAE,OAAO,EAC/C,eAAe,CAAC,EAAE,MAAM,EACnB,oBAAoB,CAAC,EAAE,MAAM,EAClC,eAAe,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,EAC9B,gBAAgB,CAAC,EAAE,MAAM,EAClB,uBAAuB,CAAC,EAAE,MAAM,EAC1C,aAAa,CAAC,EAAE,OAAO,GAClE,OAAO,CAAC,gBAAgB,CAAC;IA+C5B;;;OAGG;IAGG,oBAAoB,CACN,OAAO,EAAE,MAAM,EACxB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,UAAU,EACxC,YAAY,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACzB,MAAM,EAAE,YAAY,EACG,IAAI,CAAC,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,EACX,YAAY,CAAC,EAAE,MAAM,EACxB,SAAS,CAAC,EAAE,MAAM,EACD,0BAA0B,CAAC,EAAE,OAAO,EAC/C,eAAe,CAAC,EAAE,MAAM,EACnB,oBAAoB,CAAC,EAAE,MAAM,EAClC,eAAe,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,EAC9B,gBAAgB,CAAC,EAAE,MAAM,EAClB,uBAAuB,CAAC,EAAE,MAAM,GACrF,OAAO,CAAC,gBAAgB,CAAC;IAwB5B;;;OAGG;IACH;;;;;;OAMG;YACW,4BAA4B;IAgF1C;;;;;;OAMG;YACW,mCAAmC;IAwCjD;;;OAGG;YACW,+BAA+B;IAuD7C;;;;;;;OAOG;IAEG,gCAAgC,CACL,oBAAoB,EAAE,MAAM,EACzC,OAAO,EAAE,MAAM,EACxB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,UAAU,EACvC,SAAS,EAAE,MAAM,EACzB,MAAM,EAAE,YAAY,EAC4B,kBAAkB,CAAC,EAAE,MAAM,EACpD,IAAI,CAAC,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EACD,0BAA0B,CAAC,EAAE,OAAO,EAC/C,eAAe,CAAC,EAAE,MAAM,EACxB,eAAe,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,EAC9B,gBAAgB,CAAC,EAAE,MAAM,EAClB,uBAAuB,CAAC,EAAE,MAAM,EAC1C,aAAa,CAAC,EAAE,OAAO,GAClE,OAAO,CAAC,gBAAgB,CAAC;IA4F5B;;;;OAIG;IAEG,qBAAqB,CACL,SAAS,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACtB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,UAAU,EACpB,QAAQ,CAAC,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,OAAO,GAC9D,OAAO,CAAC,gBAAgB,CAAC;IAiE5B;;;OAGG;IAEG,oBAAoB,CACJ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAC5B,EAAE,WAAW,EAAE,EAAE,UAAU,EACD,IAAI,CAAC,EAAE,MAAM,GAC/C,OAAO,CAAC,gBAAgB,CAAC;IAyF5B;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;IAgDhC;;;;;;;;;;;;;;;;;OAiBG;YACW,sCAAsC;IAkFpD;;OAEG;IACH,OAAO,CAAC,0BAA0B;CAQrC"}
@@ -368,7 +368,9 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
368
368
  * This method is called by both the regular and system user resolvers.
369
369
  * @private
370
370
  */
371
- async executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, templateData, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts = false, createNotification = false, sourceArtifactId, sourceArtifactVersionId) {
371
+ async executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, templateData, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts = false, createNotification = false, sourceArtifactId, sourceArtifactVersionId,
372
+ /** LATENCY OPT #2: Pre-resolved conversationId avoids redundant DB load in AgentRunner */
373
+ conversationId) {
372
374
  const startTime = Date.now();
373
375
  try {
374
376
  LogStatus(`=== RUNNING AI AGENT FOR ID: ${agentId} ===`);
@@ -402,6 +404,7 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
402
404
  conversationMessages: parsedMessages,
403
405
  payload: payload ? SafeJSONParse(payload) : undefined,
404
406
  contextUser: currentUser,
407
+ sessionID: sessionId,
405
408
  onProgress: this.createProgressCallback(pubSub, sessionId, userPayload, agentRunRef),
406
409
  onStreaming: this.createStreamingCallback(pubSub, sessionId, userPayload, agentRunRef),
407
410
  lastRunId: lastRunId,
@@ -413,6 +416,7 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
413
416
  }
414
417
  }, {
415
418
  conversationDetailId: conversationDetailId, // Use existing if provided
419
+ conversationId: conversationId, // LATENCY OPT #2: pre-resolved to skip redundant load in AgentRunner
416
420
  userMessage: userMessage, // Provide user message when conversationDetailId not provided
417
421
  createArtifacts: createArtifacts || false,
418
422
  sourceArtifactId: sourceArtifactId
@@ -426,21 +430,33 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
426
430
  agentRunRef.current = result.agentRun;
427
431
  }
428
432
  const executionTime = Date.now() - startTime;
429
- // Sync feedback request if this is a continuation run (user responded via conversation)
433
+ // LATENCY OPTIMIZATION (Opt #6): These three post-execution operations are independent
434
+ // of each other — none reads the output of another. Previously they ran sequentially,
435
+ // adding their latencies together (~50ms total). Now they run in parallel via Promise.all,
436
+ // so we only pay the cost of the slowest one.
437
+ //
438
+ // 1. syncFeedbackRequestFromConversation — links a prior Chat-step feedback request to
439
+ // the new agent run so the conversation thread stays coherent.
440
+ // 2. sendFeedbackRequestNotification — sends an in-app/email/SMS notification when the
441
+ // agent paused for human input (Chat step).
442
+ // 3. createCompletionNotification — sends an in-app/email/SMS notification that the
443
+ // agent finished and created an artifact.
444
+ const postExecutionOps = [];
430
445
  if (lastRunId && result.agentRun?.ID) {
431
- await this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser);
446
+ postExecutionOps.push(this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser));
432
447
  }
433
- // Send notification if agent created a feedback request (Chat step)
434
448
  if (result.feedbackRequestId) {
435
- await this.sendFeedbackRequestNotification(result, currentUser, pubSub, userPayload);
449
+ postExecutionOps.push(this.sendFeedbackRequestNotification(result, currentUser, pubSub, userPayload));
436
450
  }
437
- // Create notification if enabled and artifact was created successfully
438
451
  if (createNotification && result.success && artifactInfo && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
439
- await this.createCompletionNotification(result.agentRun, {
452
+ postExecutionOps.push(this.createCompletionNotification(result.agentRun, {
440
453
  artifactId: artifactInfo.artifactId,
441
454
  versionId: artifactInfo.versionId,
442
455
  versionNumber: artifactInfo.versionNumber
443
- }, finalConversationDetailId, currentUser, pubSub, userPayload);
456
+ }, conversationResult.conversationId, finalConversationDetailId, currentUser, pubSub, userPayload));
457
+ }
458
+ if (postExecutionOps.length > 0) {
459
+ await Promise.all(postExecutionOps);
444
460
  }
445
461
  // Create sanitized payload for JSON serialization
446
462
  const sanitizedResult = this.sanitizeAgentResult(result);
@@ -553,21 +569,22 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
553
569
  * Create a user notification for agent completion with artifact
554
570
  * Notification includes navigation link back to the conversation
555
571
  */
556
- async createCompletionNotification(agentRun, artifactInfo, conversationDetailId, contextUser, pubSub, userPayload) {
572
+ /**
573
+ * LATENCY OPTIMIZATION (Opt #2): Now accepts conversationId directly instead of
574
+ * conversationDetailId. Previously this method loaded a ConversationDetail entity
575
+ * from the DB solely to extract its ConversationID field for building a URL — a
576
+ * redundant ~50ms DB round-trip since the caller already resolved conversationId
577
+ * when loading conversation history.
578
+ */
579
+ async createCompletionNotification(agentRun, artifactInfo, conversationId, conversationDetailId, contextUser, pubSub, userPayload) {
557
580
  try {
558
- const md = new Metadata();
559
581
  // Get agent info for notification message
560
582
  await AIEngine.Instance.Config(false, contextUser);
561
583
  const agent = AIEngine.Instance.Agents.find(a => UUIDsEqual(a.ID, agentRun.AgentID));
562
584
  const agentName = agent?.Name || 'Agent';
563
- // Load conversation detail to get conversation info
564
- const detail = await md.GetEntityObject('MJ: Conversation Details', contextUser);
565
- if (!(await detail.Load(conversationDetailId))) {
566
- throw new Error(`Failed to load conversation detail ${conversationDetailId}`);
567
- }
568
585
  // Build conversation URL for email/SMS templates
569
586
  const baseUrl = process.env.APP_BASE_URL || 'http://localhost:4201';
570
- const conversationUrl = `${baseUrl}/conversations/${detail.ConversationID}?artifact=${artifactInfo.artifactId}`;
587
+ const conversationUrl = `${baseUrl}/conversations/${conversationId}?artifact=${artifactInfo.artifactId}`;
571
588
  // Craft message based on versioning
572
589
  const message = artifactInfo.versionNumber > 1
573
590
  ? `${agentName} has finished processing and created version ${artifactInfo.versionNumber}`
@@ -582,7 +599,7 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
582
599
  message: message,
583
600
  resourceConfiguration: {
584
601
  type: 'conversation',
585
- conversationId: detail.ConversationID,
602
+ conversationId: conversationId,
586
603
  messageId: conversationDetailId,
587
604
  artifactId: artifactInfo.artifactId,
588
605
  versionId: artifactInfo.versionId,
@@ -613,7 +630,8 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
613
630
  notificationId: result.inAppNotificationId,
614
631
  action: 'create',
615
632
  title: `${agentName} completed your request`,
616
- message: message
633
+ message: message,
634
+ conversationId: conversationId
617
635
  })
618
636
  });
619
637
  LogStatus(`📡 Published notification event to client`);
@@ -738,14 +756,25 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
738
756
  };
739
757
  }
740
758
  try {
759
+ // LATENCY OPTIMIZATION (Opt #2 + #3): Load ConversationDetail once here to extract
760
+ // conversationId, then pass it downstream. Previously this record was loaded multiple
761
+ // times: once in loadConversationHistoryWithAttachments (just to get conversationId),
762
+ // once in AgentRunner (same reason), and once in createCompletionNotification. Now we
763
+ // load it a single time and thread conversationId through the call chain.
764
+ const md = new Metadata();
765
+ const currentDetail = await md.GetEntityObject('MJ: Conversation Details', currentUser);
766
+ if (!await currentDetail.Load(conversationDetailId)) {
767
+ throw new Error(`Conversation detail ${conversationDetailId} not found`);
768
+ }
769
+ const conversationId = currentDetail.ConversationID;
741
770
  // Load conversation history with attachments from DB
742
- const messages = await this.loadConversationHistoryWithAttachments(conversationDetailId, currentUser, maxHistoryMessages || 20);
771
+ const messages = await this.loadConversationHistoryWithAttachments(conversationId, currentUser, maxHistoryMessages || 20);
743
772
  // Convert to JSON string for the existing executeAIAgent method
744
773
  const messagesJson = JSON.stringify(messages);
745
774
  if (fireAndForget) {
746
775
  // Fire-and-forget mode: start execution in background, return immediately.
747
776
  // The client will receive the result via WebSocket PubSub completion event.
748
- this.executeAgentInBackground(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId);
777
+ this.executeAgentInBackground(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId, conversationId);
749
778
  LogStatus(`🔥 Fire-and-forget: Agent ${agentId} execution started in background for session ${sessionId}`);
750
779
  return {
751
780
  success: true,
@@ -754,7 +783,8 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
754
783
  }
755
784
  // Synchronous mode (default): wait for execution to complete
756
785
  return this.executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, undefined, // templateData
757
- lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId);
786
+ lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId, conversationId // LATENCY OPT #2: pass pre-resolved conversationId
787
+ );
758
788
  }
759
789
  catch (error) {
760
790
  const errorMessage = error.message || 'Unknown error loading conversation history';
@@ -914,9 +944,11 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
914
944
  * so the client receives them via WebSocket even though the HTTP response
915
945
  * has already been sent.
916
946
  */
917
- executeAgentInBackground(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts = false, createNotification = false, sourceArtifactId, sourceArtifactVersionId) {
947
+ executeAgentInBackground(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts = false, createNotification = false, sourceArtifactId, sourceArtifactVersionId,
948
+ /** LATENCY OPT #2: Pre-resolved conversationId avoids redundant DB load in AgentRunner */
949
+ conversationId) {
918
950
  // Execute in background - errors are handled within, not propagated
919
- this.executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, undefined, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts, createNotification, sourceArtifactId, sourceArtifactVersionId).catch((error) => {
951
+ this.executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, undefined, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts, createNotification, sourceArtifactId, sourceArtifactVersionId, conversationId).catch((error) => {
920
952
  // Background execution failed unexpectedly (executeAIAgent has its own try-catch,
921
953
  // so this would only fire for truly unexpected errors).
922
954
  const errorMessage = (error instanceof Error) ? error.message : 'Unknown background execution error';
@@ -938,24 +970,34 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
938
970
  /**
939
971
  * Load conversation history with attachments from database.
940
972
  * Builds ChatMessage[] with multimodal content blocks for attachments.
973
+ *
974
+ * LATENCY OPTIMIZATIONS (plans/agent-latency-optimization.md — Opts #3 and #8):
975
+ *
976
+ * Opt #3: This method now accepts conversationId directly instead of conversationDetailId.
977
+ * Previously it loaded a ConversationDetail entity object just to extract its ConversationID
978
+ * field — a redundant DB round-trip (~40ms) since the caller already has this information.
979
+ * The caller (RunAIAgentFromConversationDetail) now loads the ConversationDetail once and
980
+ * passes conversationId down.
981
+ *
982
+ * Opt #8: Switched from ResultType 'entity_object' to 'simple' with explicit Fields.
983
+ * The history query only needs ID, Role, and Message from each ConversationDetail record.
984
+ * Using 'entity_object' created full BaseEntity instances with getters/setters, dirty tracking,
985
+ * and validation — none of which are needed for read-only history assembly. The 'simple' result
986
+ * type returns plain JS objects, reducing per-record overhead (~30ms total savings).
941
987
  */
942
- async loadConversationHistoryWithAttachments(conversationDetailId, contextUser, maxMessages) {
943
- const md = new Metadata();
988
+ async loadConversationHistoryWithAttachments(conversationId, contextUser, maxMessages) {
944
989
  const rv = new RunView();
945
990
  const attachmentService = getAttachmentService();
946
- // Load the current conversation detail to get the conversation ID
947
- const currentDetail = await md.GetEntityObject('MJ: Conversation Details', contextUser);
948
- if (!await currentDetail.Load(conversationDetailId)) {
949
- throw new Error(`Conversation detail ${conversationDetailId} not found`);
950
- }
951
- const conversationId = currentDetail.ConversationID;
952
- // Load recent conversation details (messages) for this conversation
991
+ // Load recent conversation details (messages) for this conversation.
992
+ // Only fetch the three fields we actually use — ID for attachment lookups,
993
+ // Role for message routing, Message for content.
953
994
  const detailsResult = await rv.RunView({
954
995
  EntityName: 'MJ: Conversation Details',
955
996
  ExtraFilter: `ConversationID='${conversationId}'`,
956
997
  OrderBy: '__mj_CreatedAt DESC',
957
998
  MaxRows: maxMessages,
958
- ResultType: 'entity_object'
999
+ Fields: ['ID', 'Role', 'Message'],
1000
+ ResultType: 'simple'
959
1001
  }, contextUser);
960
1002
  if (!detailsResult.Success || !detailsResult.Results) {
961
1003
  throw new Error('Failed to load conversation history');