@memberjunction/server 2.128.0 → 2.130.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 (65) hide show
  1. package/dist/agents/skip-sdk.d.ts.map +1 -1
  2. package/dist/agents/skip-sdk.js +5 -1
  3. package/dist/agents/skip-sdk.js.map +1 -1
  4. package/dist/auth/index.d.ts.map +1 -1
  5. package/dist/auth/index.js +2 -3
  6. package/dist/auth/index.js.map +1 -1
  7. package/dist/config.d.ts +33 -0
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +5 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/generated/generated.d.ts +10020 -9166
  12. package/dist/generated/generated.d.ts.map +1 -1
  13. package/dist/generated/generated.js +52323 -46987
  14. package/dist/generated/generated.js.map +1 -1
  15. package/dist/generic/ResolverBase.d.ts +4 -2
  16. package/dist/generic/ResolverBase.d.ts.map +1 -1
  17. package/dist/generic/ResolverBase.js +122 -10
  18. package/dist/generic/ResolverBase.js.map +1 -1
  19. package/dist/generic/RunViewResolver.d.ts +22 -0
  20. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  21. package/dist/generic/RunViewResolver.js +171 -0
  22. package/dist/generic/RunViewResolver.js.map +1 -1
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +20 -4
  26. package/dist/index.js.map +1 -1
  27. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  28. package/dist/resolvers/AskSkipResolver.js +5 -1
  29. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  30. package/dist/resolvers/QueryResolver.d.ts +37 -0
  31. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  32. package/dist/resolvers/QueryResolver.js +304 -2
  33. package/dist/resolvers/QueryResolver.js.map +1 -1
  34. package/dist/resolvers/RunAIAgentResolver.d.ts +3 -0
  35. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  36. package/dist/resolvers/RunAIAgentResolver.js +114 -2
  37. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  38. package/dist/resolvers/RunTestResolver.d.ts +2 -2
  39. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  40. package/dist/resolvers/RunTestResolver.js +27 -8
  41. package/dist/resolvers/RunTestResolver.js.map +1 -1
  42. package/dist/resolvers/SyncDataResolver.js +5 -5
  43. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  44. package/dist/resolvers/TelemetryResolver.d.ts +120 -0
  45. package/dist/resolvers/TelemetryResolver.d.ts.map +1 -0
  46. package/dist/resolvers/TelemetryResolver.js +731 -0
  47. package/dist/resolvers/TelemetryResolver.js.map +1 -0
  48. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  49. package/dist/services/TaskOrchestrator.js.map +1 -1
  50. package/package.json +43 -42
  51. package/src/agents/skip-sdk.ts +5 -1
  52. package/src/auth/index.ts +2 -3
  53. package/src/config.ts +9 -0
  54. package/src/generated/generated.ts +29861 -26490
  55. package/src/generic/ResolverBase.ts +201 -12
  56. package/src/generic/RunViewResolver.ts +157 -3
  57. package/src/index.ts +25 -5
  58. package/src/resolvers/AskSkipResolver.ts +18 -20
  59. package/src/resolvers/QueryResolver.ts +276 -2
  60. package/src/resolvers/RunAIAgentResolver.ts +192 -4
  61. package/src/resolvers/RunAIPromptResolver.ts +1 -1
  62. package/src/resolvers/RunTestResolver.ts +20 -0
  63. package/src/resolvers/SyncDataResolver.ts +5 -5
  64. package/src/resolvers/TelemetryResolver.ts +567 -0
  65. package/src/services/TaskOrchestrator.ts +2 -1
@@ -0,0 +1,567 @@
1
+ import { Arg, Ctx, Field, InputType, Int, ObjectType, Query, Resolver, Float } from 'type-graphql';
2
+ import { AppContext } from '../types.js';
3
+ import {
4
+ TelemetryManager,
5
+ TelemetryCategory,
6
+ TelemetryInsightSeverity
7
+ } from '@memberjunction/core';
8
+
9
+ // ============================================================================
10
+ // GraphQL Types for TelemetryEvent
11
+ // ============================================================================
12
+
13
+ @ObjectType()
14
+ export class MemorySnapshotGQL {
15
+ @Field(() => Float)
16
+ heapUsed: number;
17
+
18
+ @Field(() => Float)
19
+ heapTotal: number;
20
+
21
+ @Field(() => Float)
22
+ timestamp: number;
23
+ }
24
+
25
+ @ObjectType()
26
+ export class TelemetryEventGQL {
27
+ @Field(() => String)
28
+ id: string;
29
+
30
+ @Field(() => String)
31
+ category: string;
32
+
33
+ @Field(() => String)
34
+ operation: string;
35
+
36
+ @Field(() => String)
37
+ fingerprint: string;
38
+
39
+ @Field(() => Float)
40
+ startTime: number;
41
+
42
+ @Field(() => Float, { nullable: true })
43
+ endTime?: number;
44
+
45
+ @Field(() => Float, { nullable: true })
46
+ elapsedMs?: number;
47
+
48
+ @Field(() => String, { nullable: true })
49
+ userId?: string;
50
+
51
+ @Field(() => String)
52
+ params: string; // JSON stringified
53
+
54
+ @Field(() => [String], { nullable: true })
55
+ tags?: string[];
56
+
57
+ @Field(() => String, { nullable: true })
58
+ stackTrace?: string;
59
+
60
+ @Field(() => MemorySnapshotGQL, { nullable: true })
61
+ memoryBefore?: MemorySnapshotGQL;
62
+
63
+ @Field(() => MemorySnapshotGQL, { nullable: true })
64
+ memoryAfter?: MemorySnapshotGQL;
65
+
66
+ @Field(() => String, { nullable: true })
67
+ parentEventId?: string;
68
+ }
69
+
70
+ // ============================================================================
71
+ // GraphQL Types for TelemetryPattern
72
+ // ============================================================================
73
+
74
+ @ObjectType()
75
+ export class CallerLocationGQL {
76
+ @Field(() => String)
77
+ location: string;
78
+
79
+ @Field(() => Int)
80
+ count: number;
81
+ }
82
+
83
+ @ObjectType()
84
+ export class TelemetryPatternGQL {
85
+ @Field(() => String)
86
+ fingerprint: string;
87
+
88
+ @Field(() => String)
89
+ category: string;
90
+
91
+ @Field(() => String)
92
+ operation: string;
93
+
94
+ @Field(() => String)
95
+ sampleParams: string; // JSON stringified
96
+
97
+ @Field(() => Int)
98
+ count: number;
99
+
100
+ @Field(() => Float)
101
+ totalElapsedMs: number;
102
+
103
+ @Field(() => Float)
104
+ avgElapsedMs: number;
105
+
106
+ @Field(() => Float)
107
+ minElapsedMs: number;
108
+
109
+ @Field(() => Float)
110
+ maxElapsedMs: number;
111
+
112
+ @Field(() => [CallerLocationGQL])
113
+ callerLocations: CallerLocationGQL[];
114
+
115
+ @Field(() => Float)
116
+ firstSeen: number;
117
+
118
+ @Field(() => Float)
119
+ lastSeen: number;
120
+
121
+ @Field(() => Float)
122
+ windowStartTime: number;
123
+ }
124
+
125
+ // ============================================================================
126
+ // GraphQL Types for TelemetryInsight
127
+ // ============================================================================
128
+
129
+ @ObjectType()
130
+ export class TelemetryInsightGQL {
131
+ @Field(() => String)
132
+ id: string;
133
+
134
+ @Field(() => String)
135
+ severity: string;
136
+
137
+ @Field(() => String)
138
+ analyzerName: string;
139
+
140
+ @Field(() => String)
141
+ category: string;
142
+
143
+ @Field(() => String)
144
+ title: string;
145
+
146
+ @Field(() => String)
147
+ message: string;
148
+
149
+ @Field(() => String)
150
+ suggestion: string;
151
+
152
+ @Field(() => [String])
153
+ relatedEventIds: string[];
154
+
155
+ @Field(() => String, { nullable: true })
156
+ entityName?: string;
157
+
158
+ @Field(() => String, { nullable: true })
159
+ metadata?: string; // JSON stringified
160
+
161
+ @Field(() => Float)
162
+ timestamp: number;
163
+ }
164
+
165
+ // ============================================================================
166
+ // GraphQL Types for Statistics
167
+ // ============================================================================
168
+
169
+ @ObjectType()
170
+ export class CategoryStatsGQL {
171
+ @Field(() => String)
172
+ category: string;
173
+
174
+ @Field(() => Int)
175
+ events: number;
176
+
177
+ @Field(() => Float)
178
+ avgMs: number;
179
+ }
180
+
181
+ @ObjectType()
182
+ export class TelemetryStatsGQL {
183
+ @Field(() => Int)
184
+ totalEvents: number;
185
+
186
+ @Field(() => Int)
187
+ totalPatterns: number;
188
+
189
+ @Field(() => Int)
190
+ totalInsights: number;
191
+
192
+ @Field(() => Int)
193
+ activeEvents: number;
194
+
195
+ @Field(() => [CategoryStatsGQL])
196
+ byCategory: CategoryStatsGQL[];
197
+ }
198
+
199
+ // ============================================================================
200
+ // GraphQL Types for Settings
201
+ // ============================================================================
202
+
203
+ @ObjectType()
204
+ export class CategoryOverrideGQL {
205
+ @Field(() => String)
206
+ category: string;
207
+
208
+ @Field(() => Boolean)
209
+ enabled: boolean;
210
+
211
+ @Field(() => String, { nullable: true })
212
+ level?: string;
213
+ }
214
+
215
+ @ObjectType()
216
+ export class AutoTrimSettingsGQL {
217
+ @Field(() => Boolean)
218
+ enabled: boolean;
219
+
220
+ @Field(() => Int, { nullable: true })
221
+ maxEvents?: number;
222
+
223
+ @Field(() => Int, { nullable: true })
224
+ maxAgeMs?: number;
225
+ }
226
+
227
+ @ObjectType()
228
+ export class DuplicateDetectionSettingsGQL {
229
+ @Field(() => Boolean)
230
+ enabled: boolean;
231
+
232
+ @Field(() => Int)
233
+ windowMs: number;
234
+ }
235
+
236
+ @ObjectType()
237
+ export class AnalyzerSettingsGQL {
238
+ @Field(() => Boolean)
239
+ enabled: boolean;
240
+
241
+ @Field(() => Int)
242
+ dedupeWindowMs: number;
243
+ }
244
+
245
+ @ObjectType()
246
+ export class TelemetrySettingsGQL {
247
+ @Field(() => Boolean)
248
+ enabled: boolean;
249
+
250
+ @Field(() => String)
251
+ level: string;
252
+
253
+ @Field(() => [CategoryOverrideGQL])
254
+ categoryOverrides: CategoryOverrideGQL[];
255
+
256
+ @Field(() => AutoTrimSettingsGQL)
257
+ autoTrim: AutoTrimSettingsGQL;
258
+
259
+ @Field(() => DuplicateDetectionSettingsGQL)
260
+ duplicateDetection: DuplicateDetectionSettingsGQL;
261
+
262
+ @Field(() => AnalyzerSettingsGQL)
263
+ analyzers: AnalyzerSettingsGQL;
264
+ }
265
+
266
+ // ============================================================================
267
+ // Input Types
268
+ // ============================================================================
269
+
270
+ @InputType()
271
+ export class TelemetryEventFilterInput {
272
+ @Field(() => String, { nullable: true })
273
+ category?: string;
274
+
275
+ @Field(() => String, { nullable: true })
276
+ operation?: string;
277
+
278
+ @Field(() => Float, { nullable: true })
279
+ minElapsedMs?: number;
280
+
281
+ @Field(() => Float, { nullable: true })
282
+ since?: number;
283
+
284
+ @Field(() => Int, { nullable: true })
285
+ limit?: number;
286
+ }
287
+
288
+ @InputType()
289
+ export class TelemetryPatternFilterInput {
290
+ @Field(() => String, { nullable: true })
291
+ category?: string;
292
+
293
+ @Field(() => Int, { nullable: true })
294
+ minCount?: number;
295
+
296
+ @Field(() => String, { nullable: true })
297
+ sortBy?: string;
298
+ }
299
+
300
+ @InputType()
301
+ export class TelemetryInsightFilterInput {
302
+ @Field(() => String, { nullable: true })
303
+ severity?: string;
304
+
305
+ @Field(() => String, { nullable: true })
306
+ category?: string;
307
+
308
+ @Field(() => String, { nullable: true })
309
+ entityName?: string;
310
+
311
+ @Field(() => Int, { nullable: true })
312
+ limit?: number;
313
+ }
314
+
315
+ // ============================================================================
316
+ // Resolver
317
+ // ============================================================================
318
+
319
+ @Resolver()
320
+ export class TelemetryResolver {
321
+ /**
322
+ * Get telemetry settings
323
+ * Accessible to any authenticated user - telemetry data is not sensitive
324
+ */
325
+ @Query(() => TelemetrySettingsGQL)
326
+ GetServerTelemetrySettings(@Ctx() _context: AppContext): TelemetrySettingsGQL {
327
+ const tm = TelemetryManager.Instance;
328
+ const settings = tm.Settings;
329
+
330
+ // Convert categoryOverrides map to array
331
+ const categoryOverrides: CategoryOverrideGQL[] = [];
332
+ if (settings.categoryOverrides) {
333
+ for (const [category, override] of Object.entries(settings.categoryOverrides)) {
334
+ if (override) {
335
+ categoryOverrides.push({
336
+ category,
337
+ enabled: override.enabled,
338
+ level: override.level
339
+ });
340
+ }
341
+ }
342
+ }
343
+
344
+ return {
345
+ enabled: settings.enabled,
346
+ level: settings.level,
347
+ categoryOverrides,
348
+ autoTrim: {
349
+ enabled: settings.autoTrim.enabled,
350
+ maxEvents: settings.autoTrim.maxEvents,
351
+ maxAgeMs: settings.autoTrim.maxAgeMs
352
+ },
353
+ duplicateDetection: {
354
+ enabled: settings.duplicateDetection.enabled,
355
+ windowMs: settings.duplicateDetection.windowMs
356
+ },
357
+ analyzers: {
358
+ enabled: settings.analyzers.enabled,
359
+ dedupeWindowMs: settings.analyzers.dedupeWindowMs
360
+ }
361
+ };
362
+ }
363
+
364
+ /**
365
+ * Get telemetry statistics
366
+ */
367
+ @Query(() => TelemetryStatsGQL)
368
+ GetServerTelemetryStats(@Ctx() _context: AppContext): TelemetryStatsGQL {
369
+ const tm = TelemetryManager.Instance;
370
+ const stats = tm.GetStats();
371
+
372
+ const byCategory: CategoryStatsGQL[] = Object.entries(stats.byCategory).map(
373
+ ([category, data]) => ({
374
+ category,
375
+ events: data.events,
376
+ avgMs: data.avgMs
377
+ })
378
+ );
379
+
380
+ return {
381
+ totalEvents: stats.totalEvents,
382
+ totalPatterns: stats.totalPatterns,
383
+ totalInsights: stats.totalInsights,
384
+ activeEvents: stats.activeEvents,
385
+ byCategory
386
+ };
387
+ }
388
+
389
+ /**
390
+ * Get telemetry events with optional filtering
391
+ */
392
+ @Query(() => [TelemetryEventGQL])
393
+ GetServerTelemetryEvents(
394
+ @Ctx() _context: AppContext,
395
+ @Arg('filter', () => TelemetryEventFilterInput, { nullable: true }) filter?: TelemetryEventFilterInput
396
+ ): TelemetryEventGQL[] {
397
+ const tm = TelemetryManager.Instance;
398
+
399
+ const events = tm.GetEvents({
400
+ category: filter?.category as TelemetryCategory | undefined,
401
+ operation: filter?.operation,
402
+ minElapsedMs: filter?.minElapsedMs ?? undefined,
403
+ since: filter?.since ?? undefined,
404
+ limit: filter?.limit ?? undefined
405
+ });
406
+
407
+ return events.map(e => ({
408
+ id: e.id,
409
+ category: e.category,
410
+ operation: e.operation,
411
+ fingerprint: e.fingerprint,
412
+ startTime: e.startTime,
413
+ endTime: e.endTime,
414
+ elapsedMs: e.elapsedMs,
415
+ userId: e.userId,
416
+ params: JSON.stringify(e.params),
417
+ tags: e.tags,
418
+ stackTrace: e.stackTrace,
419
+ memoryBefore: e.memoryBefore,
420
+ memoryAfter: e.memoryAfter,
421
+ parentEventId: e.parentEventId
422
+ }));
423
+ }
424
+
425
+ /**
426
+ * Get telemetry patterns with optional filtering
427
+ */
428
+ @Query(() => [TelemetryPatternGQL])
429
+ GetServerTelemetryPatterns(
430
+ @Ctx() _context: AppContext,
431
+ @Arg('filter', () => TelemetryPatternFilterInput, { nullable: true }) filter?: TelemetryPatternFilterInput
432
+ ): TelemetryPatternGQL[] {
433
+ const tm = TelemetryManager.Instance;
434
+
435
+ const patterns = tm.GetPatterns({
436
+ category: filter?.category as TelemetryCategory | undefined,
437
+ minCount: filter?.minCount ?? undefined,
438
+ sortBy: filter?.sortBy as 'count' | 'totalTime' | 'avgTime' | undefined
439
+ });
440
+
441
+ return patterns.map(p => {
442
+ // Convert Map to array
443
+ const callerLocations: CallerLocationGQL[] = [];
444
+ p.callerLocations.forEach((count, location) => {
445
+ callerLocations.push({ location, count });
446
+ });
447
+
448
+ return {
449
+ fingerprint: p.fingerprint,
450
+ category: p.category,
451
+ operation: p.operation,
452
+ sampleParams: JSON.stringify(p.sampleParams),
453
+ count: p.count,
454
+ totalElapsedMs: p.totalElapsedMs,
455
+ avgElapsedMs: p.avgElapsedMs,
456
+ minElapsedMs: p.minElapsedMs === Infinity ? 0 : p.minElapsedMs,
457
+ maxElapsedMs: p.maxElapsedMs,
458
+ callerLocations,
459
+ firstSeen: p.firstSeen,
460
+ lastSeen: p.lastSeen,
461
+ windowStartTime: p.windowStartTime
462
+ };
463
+ });
464
+ }
465
+
466
+ /**
467
+ * Get telemetry insights with optional filtering
468
+ */
469
+ @Query(() => [TelemetryInsightGQL])
470
+ GetServerTelemetryInsights(
471
+ @Ctx() _context: AppContext,
472
+ @Arg('filter', () => TelemetryInsightFilterInput, { nullable: true }) filter?: TelemetryInsightFilterInput
473
+ ): TelemetryInsightGQL[] {
474
+ const tm = TelemetryManager.Instance;
475
+
476
+ const insights = tm.GetInsights({
477
+ severity: filter?.severity as TelemetryInsightSeverity | undefined,
478
+ category: filter?.category,
479
+ entityName: filter?.entityName,
480
+ limit: filter?.limit ?? undefined
481
+ });
482
+
483
+ return insights.map(i => ({
484
+ id: i.id,
485
+ severity: i.severity,
486
+ analyzerName: i.analyzerName,
487
+ category: i.category,
488
+ title: i.title,
489
+ message: i.message,
490
+ suggestion: i.suggestion,
491
+ relatedEventIds: i.relatedEventIds,
492
+ entityName: i.entityName,
493
+ metadata: i.metadata ? JSON.stringify(i.metadata) : undefined,
494
+ timestamp: i.timestamp
495
+ }));
496
+ }
497
+
498
+ /**
499
+ * Get duplicate patterns (patterns with count >= threshold)
500
+ */
501
+ @Query(() => [TelemetryPatternGQL])
502
+ GetServerTelemetryDuplicates(
503
+ @Ctx() _context: AppContext,
504
+ @Arg('minCount', () => Int, { defaultValue: 2 }) minCount: number
505
+ ): TelemetryPatternGQL[] {
506
+ const tm = TelemetryManager.Instance;
507
+ const patterns = tm.GetDuplicates(minCount);
508
+
509
+ return patterns.map(p => {
510
+ const callerLocations: CallerLocationGQL[] = [];
511
+ p.callerLocations.forEach((count, location) => {
512
+ callerLocations.push({ location, count });
513
+ });
514
+
515
+ return {
516
+ fingerprint: p.fingerprint,
517
+ category: p.category,
518
+ operation: p.operation,
519
+ sampleParams: JSON.stringify(p.sampleParams),
520
+ count: p.count,
521
+ totalElapsedMs: p.totalElapsedMs,
522
+ avgElapsedMs: p.avgElapsedMs,
523
+ minElapsedMs: p.minElapsedMs === Infinity ? 0 : p.minElapsedMs,
524
+ maxElapsedMs: p.maxElapsedMs,
525
+ callerLocations,
526
+ firstSeen: p.firstSeen,
527
+ lastSeen: p.lastSeen,
528
+ windowStartTime: p.windowStartTime
529
+ };
530
+ });
531
+ }
532
+
533
+ /**
534
+ * Get currently active (in-progress) events
535
+ */
536
+ @Query(() => [TelemetryEventGQL])
537
+ GetServerTelemetryActiveEvents(@Ctx() _context: AppContext): TelemetryEventGQL[] {
538
+ const tm = TelemetryManager.Instance;
539
+ const events = tm.GetActiveEvents();
540
+
541
+ return events.map(e => ({
542
+ id: e.id,
543
+ category: e.category,
544
+ operation: e.operation,
545
+ fingerprint: e.fingerprint,
546
+ startTime: e.startTime,
547
+ endTime: e.endTime,
548
+ elapsedMs: e.elapsedMs,
549
+ userId: e.userId,
550
+ params: JSON.stringify(e.params),
551
+ tags: e.tags,
552
+ stackTrace: e.stackTrace,
553
+ memoryBefore: e.memoryBefore,
554
+ memoryAfter: e.memoryAfter,
555
+ parentEventId: e.parentEventId
556
+ }));
557
+ }
558
+
559
+ // Note: Server telemetry settings are configured via mj.config.cjs and cannot be changed at runtime.
560
+ // Use the telemetry config section in mj.config.cjs to enable/disable or change the level.
561
+ // Example:
562
+ // telemetry: {
563
+ // enabled: true,
564
+ // level: 'standard' // 'minimal' | 'standard' | 'verbose' | 'debug'
565
+ // }
566
+ // Or set MJ_TELEMETRY_ENABLED=false environment variable to disable.
567
+ }
@@ -1,10 +1,11 @@
1
1
  import { Metadata, RunView, UserInfo, LogError, LogStatus } from '@memberjunction/core';
2
- import { TaskEntity, TaskDependencyEntity, TaskTypeEntity, AIAgentEntityExtended, ConversationDetailEntity, ArtifactEntity, ArtifactVersionEntity, ConversationDetailArtifactEntity, UserNotificationEntity } from '@memberjunction/core-entities';
2
+ import { TaskEntity, TaskDependencyEntity, TaskTypeEntity, ConversationDetailEntity, ArtifactEntity, ArtifactVersionEntity, ConversationDetailArtifactEntity, UserNotificationEntity } from '@memberjunction/core-entities';
3
3
  import { AgentRunner } from '@memberjunction/ai-agents';
4
4
  import { ChatMessageRole } from '@memberjunction/ai';
5
5
  import { PubSubEngine } from 'type-graphql';
6
6
  import { UserPayload } from '../types.js';
7
7
  import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
8
+ import { AIAgentEntityExtended } from '@memberjunction/ai-core-plus';
8
9
 
9
10
  /**
10
11
  * Task definition from LLM response