@memberjunction/server 6.1.0-edge.0 → 6.1.0-edge.2
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/auth/exampleNewUserSubClass.js +1 -1
- package/dist/auth/exampleNewUserSubClass.js.map +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +9 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +1 -1
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.js +1 -1
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/context.js +2 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +208 -646
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +1123 -3773
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +19 -3
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +51 -17
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +100 -42
- package/dist/index.js.map +1 -1
- package/dist/logging/secretRedactor.d.ts +9 -0
- package/dist/logging/secretRedactor.d.ts.map +1 -1
- package/dist/logging/secretRedactor.js +39 -0
- package/dist/logging/secretRedactor.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +5 -0
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
- package/dist/middleware/BaseServerMiddleware.js +5 -0
- package/dist/middleware/BaseServerMiddleware.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +12 -0
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.js +1 -1
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +9 -0
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +3 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
- package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +10 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +5 -0
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/SearchEntitiesResolver.js +1 -1
- package/dist/resolvers/SearchEntitiesResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +2 -2
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +85 -0
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -0
- package/dist/resolvers/TaskGraphFrameResolver.js +163 -0
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -0
- package/dist/resolvers/UserFavoriteResolver.js +1 -1
- package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +1 -1
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts +0 -4
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +1 -15
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/SignatureWebhookHandler.js +1 -1
- package/dist/rest/SignatureWebhookHandler.js.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts +26 -0
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -0
- package/dist/services/DurableEntityActionTaskSubmitter.js +55 -0
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -0
- package/dist/services/ScheduledJobsService.js +1 -1
- package/dist/services/ScheduledJobsService.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.d.ts +33 -0
- package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -0
- package/dist/services/StartTaskGraphDispatcher.js +46 -0
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -0
- package/dist/services/TaskGraphActionRunner.d.ts +34 -0
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphActionRunner.js +95 -0
- package/dist/services/TaskGraphActionRunner.js.map +1 -0
- package/dist/services/TaskGraphAgentRunner.d.ts +28 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphAgentRunner.js +104 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +70 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js +162 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -0
- package/dist/services/TaskGraphPromptRunner.d.ts +31 -0
- package/dist/services/TaskGraphPromptRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphPromptRunner.js +134 -0
- package/dist/services/TaskGraphPromptRunner.js.map +1 -0
- package/dist/services/TaskGraphProviderFactory.d.ts +39 -0
- package/dist/services/TaskGraphProviderFactory.d.ts.map +1 -0
- package/dist/services/TaskGraphProviderFactory.js +28 -0
- package/dist/services/TaskGraphProviderFactory.js.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.js +1 -1
- package/dist/telephony/RingCentralTelephonyService.js.map +1 -1
- package/dist/telephony/TwilioTelephonyRouter.js +1 -1
- package/dist/telephony/TwilioTelephonyRouter.js.map +1 -1
- package/dist/telephony/VonageTelephonyRouter.js +1 -1
- package/dist/telephony/VonageTelephonyRouter.js.map +1 -1
- package/package.json +92 -89
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +1 -1
- package/src/__tests__/ResolverBase.frozenRecordMapping.test.ts +133 -0
- package/src/__tests__/ResolverBase.transportMapping.test.ts +289 -0
- package/src/__tests__/SessionManager.test.ts +1 -1
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +249 -0
- package/src/__tests__/TaskGraphFrameResolver.test.ts +136 -0
- package/src/__tests__/WidgetSessionService.test.ts +1 -1
- package/src/__tests__/databaseAbstraction.test.ts +1 -1
- package/src/__tests__/magicLinkService.provisionLog.test.ts +1 -1
- package/src/__tests__/resolverBase.rls.test.ts +3 -0
- package/src/__tests__/secretRedactor.failOpen.test.ts +135 -0
- package/src/__tests__/widgetGuestElevation.test.ts +1 -1
- package/src/auth/exampleNewUserSubClass.ts +1 -1
- package/src/auth/index.ts +10 -6
- package/src/auth/magicLink/MagicLinkService.ts +1 -1
- package/src/auth/newUsers.ts +1 -1
- package/src/context.ts +2 -2
- package/src/generated/generated.ts +955 -2735
- package/src/generic/ResolverBase.ts +52 -17
- package/src/index.ts +108 -43
- package/src/logging/secretRedactor.ts +43 -0
- package/src/middleware/BaseServerMiddleware.ts +5 -0
- package/src/realtimeWidget/WidgetSessionService.ts +1 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -1
- package/src/resolvers/ActionResolver.ts +13 -0
- package/src/resolvers/ComponentRegistryResolver.ts +1 -1
- package/src/resolvers/EntityPermissionResolver.ts +1 -1
- package/src/resolvers/IntegrationDiscoveryResolver.ts +12 -2
- package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
- package/src/resolvers/RealtimeClientSessionResolver.ts +13 -0
- package/src/resolvers/SearchEntitiesResolver.ts +1 -1
- package/src/resolvers/SqlLoggingConfigResolver.ts +1 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +2 -2
- package/src/resolvers/TaskGraphFrameResolver.ts +160 -0
- package/src/resolvers/UserFavoriteResolver.ts +1 -1
- package/src/rest/OAuthCallbackHandler.ts +1 -1
- package/src/rest/RESTEndpointHandler.ts +1 -15
- package/src/rest/SignatureWebhookHandler.ts +1 -1
- package/src/services/DurableEntityActionTaskSubmitter.ts +78 -0
- package/src/services/ScheduledJobsService.ts +1 -1
- package/src/services/StartTaskGraphDispatcher.ts +78 -0
- package/src/services/TaskGraphActionRunner.ts +99 -0
- package/src/services/TaskGraphAgentRunner.ts +115 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +181 -0
- package/src/services/TaskGraphPromptRunner.ts +144 -0
- package/src/services/TaskGraphProviderFactory.ts +53 -0
- package/src/telephony/RingCentralTelephonyService.ts +1 -1
- package/src/telephony/TwilioTelephonyRouter.ts +1 -1
- package/src/telephony/VonageTelephonyRouter.ts +1 -1
- package/dist/resolvers/ReportResolver.d.ts +0 -24
- package/dist/resolvers/ReportResolver.d.ts.map +0 -1
- package/dist/resolvers/ReportResolver.js +0 -198
- package/dist/resolvers/ReportResolver.js.map +0 -1
- package/dist/resolvers/TaskResolver.d.ts +0 -31
- package/dist/resolvers/TaskResolver.d.ts.map +0 -1
- package/dist/resolvers/TaskResolver.js +0 -157
- package/dist/resolvers/TaskResolver.js.map +0 -1
- package/dist/services/TaskOrchestrator.d.ts +0 -130
- package/dist/services/TaskOrchestrator.d.ts.map +0 -1
- package/dist/services/TaskOrchestrator.js +0 -692
- package/dist/services/TaskOrchestrator.js.map +0 -1
- package/src/resolvers/ReportResolver.ts +0 -165
- package/src/resolvers/TaskResolver.ts +0 -148
- package/src/resolvers/__tests__/ReportResolver.test.ts +0 -279
- package/src/services/TaskOrchestrator.ts +0 -873
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MJServer's implementation of the task-graph `TaskContinuationDeliverer` seam.
|
|
3
|
+
*
|
|
4
|
+
* **The dispatcher shipped with nowhere to deliver.** `StartTaskGraphDispatcher` constructed it
|
|
5
|
+
* without a deliverer at all, so a graph that finished logged its outcome, marked itself delivered,
|
|
6
|
+
* and said nothing to the conversation that asked for it. Durable execution nobody hears about is
|
|
7
|
+
* half a promise — the same shape as Phase 2's dispatcher that ran nothing.
|
|
8
|
+
*
|
|
9
|
+
* Posting into a conversation is a host concern by design: the task-graph package must not depend on
|
|
10
|
+
* the conversation layer, and cannot depend on the agent framework at all without a cycle (agents
|
|
11
|
+
* submit graphs). This adapter is where those dependencies are allowed to live.
|
|
12
|
+
*
|
|
13
|
+
* @module @memberjunction/server
|
|
14
|
+
*/
|
|
15
|
+
import { LogError, LogStatus, type IMetadataProvider, type UserInfo } from '@memberjunction/core';
|
|
16
|
+
import { MJAIAgentRunEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';
|
|
17
|
+
import { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';
|
|
18
|
+
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
19
|
+
import { ChatMessageRole } from '@memberjunction/ai';
|
|
20
|
+
import type { ProviderFactory, TaskContinuationDeliverer, TaskContinuationParams } from '@memberjunction/task-graph';
|
|
21
|
+
|
|
22
|
+
/** How many per-task lines a posted summary shows before collapsing the rest into a count. */
|
|
23
|
+
const MAX_LISTED_TASKS = 20;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Delivers a finished graph's outcome — as a conversation message, or by starting the submitting
|
|
27
|
+
* agent a fresh turn.
|
|
28
|
+
*
|
|
29
|
+
* **The chain is bounded, and that is what took a schema change to make true.** A graph may declare
|
|
30
|
+
* `continuation: 'reinvoke'`, and the agent it restarts can emit another graph, which can reinvoke
|
|
31
|
+
* again. The guard for that (`MAX_REINVOKE_DEPTH`) shipped in Phase 3, but the number it compared
|
|
32
|
+
* was permanently zero: `TaskGraphService.Submit` reads `ReinvokeDepth` from its caller, and a
|
|
33
|
+
* reinvoked agent had no way to know it *was* a continuation. `AIAgentRun.ContinuationDepth` closes
|
|
34
|
+
* that loop — this stamps depth + 1 on the run it starts, `BaseAgent` passes it into any graph that
|
|
35
|
+
* run submits, and the cap finally compares against something real.
|
|
36
|
+
*/
|
|
37
|
+
export class TaskGraphContinuationDeliverer implements TaskContinuationDeliverer {
|
|
38
|
+
/**
|
|
39
|
+
* @param providerFactory mints a fresh provider per delivery, for the same reason the dispatcher
|
|
40
|
+
* does: deliveries run outside any request and concurrently with task execution, so
|
|
41
|
+
* sharing one provider would share one transaction scope across unrelated work.
|
|
42
|
+
*/
|
|
43
|
+
constructor(
|
|
44
|
+
private readonly providerFactory: ProviderFactory,
|
|
45
|
+
private readonly contextUser: UserInfo,
|
|
46
|
+
) {}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Posts the roll-up as an AI-role message in the graph's conversation.
|
|
50
|
+
*
|
|
51
|
+
* Never throws. The dispatcher calls this inside the compare-and-swap that marks a completion
|
|
52
|
+
* delivered; an error escaping would either abort that guard or leave the graph looking
|
|
53
|
+
* undelivered and re-notifying on every later sweep.
|
|
54
|
+
*/
|
|
55
|
+
public async PostMessage(params: TaskContinuationParams): Promise<void> {
|
|
56
|
+
try {
|
|
57
|
+
if (!params.ConversationDetailID) {
|
|
58
|
+
// A graph submitted headlessly — a schedule, an entity-change trigger, an API call —
|
|
59
|
+
// has no conversation to answer. Not an error; most workflows are in this shape.
|
|
60
|
+
LogStatus(`[TaskGraphContinuationDeliverer] "${params.WorkflowName}" finished with no conversation to post to.`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const provider = await this.providerFactory.CreateProvider();
|
|
65
|
+
const conversationID = await this.resolveConversationID(params.ConversationDetailID, provider);
|
|
66
|
+
if (!conversationID) {
|
|
67
|
+
LogError(
|
|
68
|
+
`[TaskGraphContinuationDeliverer] Conversation detail ${params.ConversationDetailID} could not be loaded — ` +
|
|
69
|
+
`"${params.WorkflowName}" has nowhere to post its outcome.`
|
|
70
|
+
);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const detail = await provider.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', this.contextUser);
|
|
75
|
+
detail.NewRecord();
|
|
76
|
+
detail.ConversationID = conversationID;
|
|
77
|
+
detail.Role = 'AI';
|
|
78
|
+
detail.Status = 'Complete';
|
|
79
|
+
detail.HiddenToUser = false;
|
|
80
|
+
detail.Message = this.renderMessage(params);
|
|
81
|
+
|
|
82
|
+
if (!(await detail.Save())) {
|
|
83
|
+
LogError(
|
|
84
|
+
`[TaskGraphContinuationDeliverer] Could not post the outcome of "${params.WorkflowName}": ` +
|
|
85
|
+
`${detail.LatestResult?.CompleteMessage ?? 'unknown error'}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
LogError(`[TaskGraphContinuationDeliverer] Posting the outcome of "${params.WorkflowName}" threw`, undefined, e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Starts the submitting agent a fresh turn carrying the graph's outcome.
|
|
95
|
+
*
|
|
96
|
+
* Never throws, for the same reason `PostMessage` does not: the dispatcher calls this inside the
|
|
97
|
+
* compare-and-swap that marks a completion delivered.
|
|
98
|
+
*
|
|
99
|
+
* The new run records `ReinvokeDepth + 1` so the chain is bounded. The dispatcher has already
|
|
100
|
+
* refused to call this when the cap is reached — it degrades to `PostMessage` — so arriving here
|
|
101
|
+
* means there is budget left, and stamping the depth is what keeps that true for the next hop.
|
|
102
|
+
*/
|
|
103
|
+
public async Reinvoke(params: TaskContinuationParams): Promise<void> {
|
|
104
|
+
try {
|
|
105
|
+
if (!params.SubmittedByAgentRunID) {
|
|
106
|
+
// Nothing to restart. A graph with no submitting run came from a schedule or a
|
|
107
|
+
// trigger, where "continue the conversation" has no meaning.
|
|
108
|
+
LogStatus(`[TaskGraphContinuationDeliverer] "${params.WorkflowName}" asked to reinvoke but records no submitting agent run — posting instead.`);
|
|
109
|
+
await this.PostMessage(params);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const provider = await this.providerFactory.CreateProvider();
|
|
114
|
+
const priorRun = await provider.GetEntityObject<MJAIAgentRunEntity>('MJ: AI Agent Runs', this.contextUser);
|
|
115
|
+
if (!(await priorRun.Load(params.SubmittedByAgentRunID))) {
|
|
116
|
+
LogError(`[TaskGraphContinuationDeliverer] Submitting run ${params.SubmittedByAgentRunID} could not be loaded — posting "${params.WorkflowName}" instead.`);
|
|
117
|
+
await this.PostMessage(params);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const agent = await provider.GetEntityObject<MJAIAgentEntityExtended>('MJ: AI Agents', this.contextUser);
|
|
122
|
+
if (!(await agent.Load(priorRun.AgentID))) {
|
|
123
|
+
LogError(`[TaskGraphContinuationDeliverer] Agent ${priorRun.AgentID} could not be loaded — posting "${params.WorkflowName}" instead.`);
|
|
124
|
+
await this.PostMessage(params);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await new AgentRunner().RunAgent({
|
|
129
|
+
agent,
|
|
130
|
+
conversationMessages: [{ role: ChatMessageRole.user, content: this.renderMessage(params) }],
|
|
131
|
+
contextUser: this.contextUser,
|
|
132
|
+
conversationDetailId: params.ConversationDetailID ?? undefined,
|
|
133
|
+
// The load-bearing value: without it the next graph this run submits restarts the
|
|
134
|
+
// chain at zero and the cap never fires.
|
|
135
|
+
continuationDepth: params.ReinvokeDepth + 1,
|
|
136
|
+
});
|
|
137
|
+
} catch (e) {
|
|
138
|
+
LogError(`[TaskGraphContinuationDeliverer] Reinvoking for "${params.WorkflowName}" threw`, undefined, e);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The conversation a detail belongs to. */
|
|
143
|
+
private async resolveConversationID(conversationDetailID: string, provider: IMetadataProvider): Promise<string | null> {
|
|
144
|
+
const detail = await provider.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', this.contextUser);
|
|
145
|
+
return (await detail.Load(conversationDetailID)) ? detail.ConversationID : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Renders the outcome as Markdown.
|
|
150
|
+
*
|
|
151
|
+
* Per-task lines rather than one aggregate status: a graph where nine of ten steps succeeded is a
|
|
152
|
+
* materially different message from one that failed outright, and the roll-up alone cannot say
|
|
153
|
+
* which step went wrong. Long graphs truncate so a fifty-step workflow does not bury the
|
|
154
|
+
* conversation — the task rows remain the complete record.
|
|
155
|
+
*/
|
|
156
|
+
private renderMessage(params: TaskContinuationParams): string {
|
|
157
|
+
const lines = [`**${params.WorkflowName}** finished.`, '', params.Summary];
|
|
158
|
+
|
|
159
|
+
if (params.Tasks.length > 0) {
|
|
160
|
+
lines.push('');
|
|
161
|
+
for (const task of params.Tasks.slice(0, MAX_LISTED_TASKS)) {
|
|
162
|
+
const detail = task.ErrorMessage ?? task.Summary;
|
|
163
|
+
lines.push(`- ${this.statusIcon(task.Status)} **${task.Name}** — ${task.Status}${detail ? `: ${detail}` : ''}`);
|
|
164
|
+
}
|
|
165
|
+
if (params.Tasks.length > MAX_LISTED_TASKS) {
|
|
166
|
+
lines.push(`- …and ${params.Tasks.length - MAX_LISTED_TASKS} more`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return lines.join('\n');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private statusIcon(status: string): string {
|
|
174
|
+
switch (status) {
|
|
175
|
+
case 'Complete': return '✅';
|
|
176
|
+
case 'Failed': return '❌';
|
|
177
|
+
case 'Cancelled': return '⊘';
|
|
178
|
+
default: return '•';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MJServer's implementation of the task-graph `TaskPromptRunner` seam.
|
|
3
|
+
*
|
|
4
|
+
* The third sibling of {@link TaskGraphAgentRunner} and {@link TaskGraphActionRunner}, and here for
|
|
5
|
+
* the same reason: the dispatcher knows *when* a prompt-assigned node should run and nothing about
|
|
6
|
+
* how a prompt executes. The AI engine depends on the entity layer the dispatcher also builds on, so
|
|
7
|
+
* importing it into `@memberjunction/task-graph` would make every consumer of durable execution load
|
|
8
|
+
* the prompt engine too.
|
|
9
|
+
*
|
|
10
|
+
* **Why prompt nodes matter enough to have their own runner.** The `User Onboarding Flow Agent`
|
|
11
|
+
* ships with six of them. Until this existed, every one of its steps was refused at submission and
|
|
12
|
+
* the agent could not run at all — the refusal was correct (a persisted step nothing can execute
|
|
13
|
+
* waits forever) but it made a shipped workflow unusable rather than merely unfinished.
|
|
14
|
+
*
|
|
15
|
+
* @module @memberjunction/server
|
|
16
|
+
*/
|
|
17
|
+
import { AIPromptRunner } from '@memberjunction/ai-prompts';
|
|
18
|
+
import { AIEngine } from '@memberjunction/aiengine';
|
|
19
|
+
import { AIPromptParams } from '@memberjunction/ai-core-plus';
|
|
20
|
+
import { LogError } from '@memberjunction/core';
|
|
21
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
22
|
+
import type { TaskPromptRunner, TaskPromptRunParams, TaskPromptRunResult } from '@memberjunction/task-graph';
|
|
23
|
+
|
|
24
|
+
/** The placeholder a workflow prompt uses to receive the payload (plan §5.7). */
|
|
25
|
+
const CURRENT_PAYLOAD_PLACEHOLDER = '_CURRENT_PAYLOAD';
|
|
26
|
+
|
|
27
|
+
export class TaskGraphPromptRunner implements TaskPromptRunner {
|
|
28
|
+
public async RunPromptForTask(params: TaskPromptRunParams): Promise<TaskPromptRunResult> {
|
|
29
|
+
try {
|
|
30
|
+
await AIEngine.Instance.Config(false, params.ContextUser);
|
|
31
|
+
const prompt = AIEngine.Instance.Prompts.find((p) => UUIDsEqual(p.ID, params.PromptID));
|
|
32
|
+
if (!prompt) {
|
|
33
|
+
return { Success: false, ErrorMessage: `Prompt ${params.PromptID} is not in the engine's metadata.` };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const promptParams = new AIPromptParams();
|
|
37
|
+
promptParams.prompt = prompt;
|
|
38
|
+
promptParams.contextUser = params.ContextUser;
|
|
39
|
+
promptParams.data = this.buildData(params);
|
|
40
|
+
|
|
41
|
+
const result = await new AIPromptRunner().ExecutePrompt(promptParams);
|
|
42
|
+
if (!result?.success) {
|
|
43
|
+
return {
|
|
44
|
+
Success: false,
|
|
45
|
+
ErrorMessage: result?.errorMessage ?? 'The prompt did not return a result.',
|
|
46
|
+
PromptRunID: result?.promptRun?.ID,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const parsed = this.parseResponse(result.result);
|
|
51
|
+
return {
|
|
52
|
+
Success: true,
|
|
53
|
+
Output: parsed.payload,
|
|
54
|
+
PromptRunID: result.promptRun?.ID,
|
|
55
|
+
ChatMessage: parsed.chatMessage,
|
|
56
|
+
};
|
|
57
|
+
} catch (e) {
|
|
58
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
59
|
+
LogError(`[TaskGraphPromptRunner] Task ${params.TaskID} failed: ${message}`);
|
|
60
|
+
return { Success: false, ErrorMessage: message };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What the prompt's template can see.
|
|
66
|
+
*
|
|
67
|
+
* The payload arrives under a **placeholder name** rather than being spread across the top level,
|
|
68
|
+
* so a workflow author writes `{{ _CURRENT_PAYLOAD }}` and gets the whole thing — and a payload
|
|
69
|
+
* key can never collide with a template parameter and silently win.
|
|
70
|
+
*
|
|
71
|
+
* `flowContext` carries what the step knows about its own position: what its prerequisites
|
|
72
|
+
* produced. Without it a prompt is reasoning about the payload alone and cannot refer to the step
|
|
73
|
+
* immediately before it.
|
|
74
|
+
*/
|
|
75
|
+
private buildData(params: TaskPromptRunParams): Record<string, unknown> {
|
|
76
|
+
const payload = params.InputPayload && typeof params.InputPayload === 'object'
|
|
77
|
+
? params.InputPayload as Record<string, unknown>
|
|
78
|
+
: {};
|
|
79
|
+
|
|
80
|
+
const dependencyOutputs: Record<string, unknown> = {};
|
|
81
|
+
for (const [taskID, output] of params.DependencyOutputs) dependencyOutputs[taskID] = output;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
...(params.TemplateParameters ?? {}),
|
|
85
|
+
// JSON, not the object. A template writes `{{ _CURRENT_PAYLOAD }}` and expects to SEE
|
|
86
|
+
// the payload; handing the renderer an object yields "[object Object]", and the model
|
|
87
|
+
// then answers about a value it was never shown. That failure is invisible from the
|
|
88
|
+
// outside — the prompt run succeeds, the response is well-formed, and it is about
|
|
89
|
+
// nothing. It is what kept the Content Pipeline reviewer rejecting a draft it could not
|
|
90
|
+
// read, on every iteration, forever.
|
|
91
|
+
[CURRENT_PAYLOAD_PLACEHOLDER]: JSON.stringify(payload, null, 2),
|
|
92
|
+
flowContext: JSON.stringify({ dependencyOutputs }, null, 2),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reads the model's response as a payload update, and notices when it wants to stop.
|
|
98
|
+
*
|
|
99
|
+
* **A prompt is the only node kind that can end the workflow early**, because it is the only one
|
|
100
|
+
* doing open-ended reasoning: it can conclude the remaining steps are unnecessary. That is
|
|
101
|
+
* expressed as a Chat-shaped response, and the dispatcher honours it by skipping what remains and
|
|
102
|
+
* settling the parent Complete — rather than treating an early finish as an abandoned graph.
|
|
103
|
+
*
|
|
104
|
+
* Non-JSON is not an error. A prompt that answers in prose has still answered; it simply carries
|
|
105
|
+
* no structured update, so it becomes a message rather than a payload merge.
|
|
106
|
+
*/
|
|
107
|
+
private parseResponse(raw: unknown): { payload?: Record<string, unknown>; chatMessage?: string } {
|
|
108
|
+
if (raw == null) return {};
|
|
109
|
+
|
|
110
|
+
if (typeof raw === 'object') {
|
|
111
|
+
return this.interpret(raw as Record<string, unknown>);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (typeof raw === 'string') {
|
|
115
|
+
const text = raw.trim();
|
|
116
|
+
const fenced = text.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
|
|
117
|
+
try {
|
|
118
|
+
const parsed: unknown = JSON.parse(fenced);
|
|
119
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
120
|
+
return this.interpret(parsed as Record<string, unknown>);
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
// Prose, not JSON — see above.
|
|
124
|
+
}
|
|
125
|
+
return { chatMessage: text };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Separates "here is an update" from "we are done, tell them this". */
|
|
132
|
+
private interpret(obj: Record<string, unknown>): { payload?: Record<string, unknown>; chatMessage?: string } {
|
|
133
|
+
const nextStep = obj['nextStep'] as { type?: string } | undefined;
|
|
134
|
+
const wantsChat = nextStep?.type === 'Chat' || obj['taskComplete'] === true;
|
|
135
|
+
const message = typeof obj['message'] === 'string' ? obj['message'] : undefined;
|
|
136
|
+
|
|
137
|
+
if (wantsChat && message) {
|
|
138
|
+
// The payload still merges: a prompt that both concluded and produced data should not
|
|
139
|
+
// lose the data because it also had something to say.
|
|
140
|
+
return { payload: obj, chatMessage: message };
|
|
141
|
+
}
|
|
142
|
+
return { payload: obj };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MJServer's implementation of the task-graph `ProviderFactory` seam.
|
|
3
|
+
*
|
|
4
|
+
* The durable dispatcher runs outside any HTTP request and executes tasks concurrently, so it must
|
|
5
|
+
* never share one provider — and therefore one transaction scope and one set of entity instances —
|
|
6
|
+
* across parallel work. It needs a way to mint providers on demand.
|
|
7
|
+
*
|
|
8
|
+
* Rather than have `@memberjunction/task-graph` import MJServer to get at that machinery (which
|
|
9
|
+
* would invert the dependency: MJServer depends on task-graph, not the reverse), the package
|
|
10
|
+
* declares a `ProviderFactory` interface and the host supplies an implementation. This is that
|
|
11
|
+
* implementation.
|
|
12
|
+
*
|
|
13
|
+
* It mints exactly what the per-request path already mints — a fresh provider over the **shared
|
|
14
|
+
* connection pool**, with metadata reuse — which is proven cheap at request scale and is why the
|
|
15
|
+
* plan chose this mechanism rather than inventing a dispatcher-specific one. The pool remains the
|
|
16
|
+
* real concurrency governor; the dispatcher's own `MaxConcurrentTasks` sits below it.
|
|
17
|
+
*
|
|
18
|
+
* @module @memberjunction/server
|
|
19
|
+
*/
|
|
20
|
+
import { DatabaseProviderBase, IMetadataProvider } from '@memberjunction/core';
|
|
21
|
+
import type { ProviderFactory } from '@memberjunction/task-graph';
|
|
22
|
+
import { SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
23
|
+
import sql from 'mssql';
|
|
24
|
+
import { mj_core_schema } from '../config.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates providers for the dispatcher, one per task execution.
|
|
28
|
+
*
|
|
29
|
+
* SQL Server path only for now, matching where the dispatcher is hosted. The PostgreSQL branch of
|
|
30
|
+
* `createPerRequestProviders` is deliberately not duplicated here — when PG hosting is needed, the
|
|
31
|
+
* right move is to lift the shared branch out of `context.ts` rather than fork it a second time.
|
|
32
|
+
*/
|
|
33
|
+
export class TaskGraphProviderFactory implements ProviderFactory {
|
|
34
|
+
constructor(private readonly pool: sql.ConnectionPool) {}
|
|
35
|
+
|
|
36
|
+
public async CreateProvider(): Promise<IMetadataProvider> {
|
|
37
|
+
// `loadIfNeeded = false` (the trailing false) reuses already-loaded metadata rather than
|
|
38
|
+
// re-reading it per provider — the difference between "cheap enough to do per task" and
|
|
39
|
+
// "prohibitively expensive".
|
|
40
|
+
const config = new SQLServerProviderConfigData(this.pool, mj_core_schema, 0, undefined, undefined, false);
|
|
41
|
+
const provider = new SQLServerDataProvider();
|
|
42
|
+
await provider.Config(config);
|
|
43
|
+
return provider as unknown as IMetadataProvider;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Convenience for hosts that already hold a pool. */
|
|
48
|
+
export function CreateTaskGraphProviderFactory(pool: sql.ConnectionPool): ProviderFactory {
|
|
49
|
+
return new TaskGraphProviderFactory(pool);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Re-exported for hosts that need the concrete type. */
|
|
53
|
+
export type { DatabaseProviderBase };
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import { RunView, UserInfo, IMetadataProvider, Metadata, LogError, LogStatus } from '@memberjunction/core';
|
|
21
21
|
import type { MJAIBridgeAgentIdentityEntity, MJAIBridgeProviderEntity } from '@memberjunction/core-entities';
|
|
22
|
-
import { UserCache } from '@memberjunction/
|
|
22
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
23
23
|
import { AIBridgeEngine } from '@memberjunction/ai-bridge-server';
|
|
24
24
|
import { CreateBridgeRealtimeSession } from '@memberjunction/ai-agents';
|
|
25
25
|
import {
|
|
@@ -20,7 +20,7 @@ import { Router, urlencoded, type Request, type Response } from 'express';
|
|
|
20
20
|
import { WebSocketServer, type WebSocket } from 'ws';
|
|
21
21
|
import { RegisterMediaUpgradeRoute } from './media-upgrade-router.js';
|
|
22
22
|
import { LogError, LogStatus, UserInfo, IMetadataProvider, Metadata } from '@memberjunction/core';
|
|
23
|
-
import { UserCache } from '@memberjunction/
|
|
23
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
24
24
|
import {
|
|
25
25
|
verifyTwilioSignature,
|
|
26
26
|
resolveInboundCall,
|
|
@@ -26,7 +26,7 @@ import { WebSocketServer, type WebSocket } from 'ws';
|
|
|
26
26
|
import type { IncomingMessage } from 'node:http';
|
|
27
27
|
import { RegisterMediaUpgradeRoute } from './media-upgrade-router.js';
|
|
28
28
|
import { LogError, LogStatus, UserInfo, IMetadataProvider, Metadata } from '@memberjunction/core';
|
|
29
|
-
import { UserCache } from '@memberjunction/
|
|
29
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
30
30
|
import {
|
|
31
31
|
verifyVonageSignature,
|
|
32
32
|
verifyVonageJwt,
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { AppContext } from '../types.js';
|
|
2
|
-
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
3
|
-
export declare class RunReportResultType {
|
|
4
|
-
ReportID: string;
|
|
5
|
-
Success: boolean;
|
|
6
|
-
Results: string;
|
|
7
|
-
RowCount: number;
|
|
8
|
-
ExecutionTime: number;
|
|
9
|
-
ErrorMessage: string;
|
|
10
|
-
}
|
|
11
|
-
export declare class CreateReportResultType {
|
|
12
|
-
ReportID: string;
|
|
13
|
-
ReportName: string;
|
|
14
|
-
Success: boolean;
|
|
15
|
-
ErrorMessage: string;
|
|
16
|
-
}
|
|
17
|
-
export declare class ReportResolverExtended extends ResolverBase {
|
|
18
|
-
GetReportData(ReportID: string, context: AppContext): Promise<RunReportResultType>;
|
|
19
|
-
/**
|
|
20
|
-
* This mutation will create a new report from a conversation detail ID
|
|
21
|
-
*/
|
|
22
|
-
CreateReportFromConversationDetailID(ConversationDetailID: string, { dataSource, userPayload, providers }: AppContext): Promise<CreateReportResultType>;
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=ReportResolver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ReportResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/ReportResolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,qBACa,mBAAmB;IAE9B,QAAQ,EAAE,MAAM,CAAC;IAGjB,OAAO,EAAE,OAAO,CAAC;IAGjB,OAAO,EAAE,MAAM,CAAC;IAGhB,QAAQ,EAAE,MAAM,CAAC;IAGjB,aAAa,EAAE,MAAM,CAAC;IAGtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBACa,sBAAsB;IAEjC,QAAQ,EAAE,MAAM,CAAC;IAGjB,UAAU,EAAE,MAAM,CAAC;IAGnB,OAAO,EAAE,OAAO,CAAC;IAGjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBACa,sBAAuB,SAAQ,YAAY;IAEhD,aAAa,CAAgC,QAAQ,EAAE,MAAM,EAAS,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAkB9H;;OAEG;IAEG,oCAAoC,CACG,oBAAoB,EAAE,MAAM,EAChE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,UAAU,GACxD,OAAO,CAAC,sBAAsB,CAAC;CA0FnC"}
|
|
@@ -1,198 +0,0 @@
|
|
|
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 { RunReport } from '@memberjunction/core';
|
|
14
|
-
import { Arg, Ctx, Field, Mutation, ObjectType, Query, Resolver } from 'type-graphql';
|
|
15
|
-
import { DataContext } from '@memberjunction/data-context';
|
|
16
|
-
import { z } from 'zod';
|
|
17
|
-
import mssql from 'mssql';
|
|
18
|
-
import { GetReadOnlyProvider, GetReadWriteProvider } from '../util.js';
|
|
19
|
-
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
20
|
-
let RunReportResultType = class RunReportResultType {
|
|
21
|
-
};
|
|
22
|
-
__decorate([
|
|
23
|
-
Field(),
|
|
24
|
-
__metadata("design:type", String)
|
|
25
|
-
], RunReportResultType.prototype, "ReportID", void 0);
|
|
26
|
-
__decorate([
|
|
27
|
-
Field(),
|
|
28
|
-
__metadata("design:type", Boolean)
|
|
29
|
-
], RunReportResultType.prototype, "Success", void 0);
|
|
30
|
-
__decorate([
|
|
31
|
-
Field(),
|
|
32
|
-
__metadata("design:type", String)
|
|
33
|
-
], RunReportResultType.prototype, "Results", void 0);
|
|
34
|
-
__decorate([
|
|
35
|
-
Field(),
|
|
36
|
-
__metadata("design:type", Number)
|
|
37
|
-
], RunReportResultType.prototype, "RowCount", void 0);
|
|
38
|
-
__decorate([
|
|
39
|
-
Field(),
|
|
40
|
-
__metadata("design:type", Number)
|
|
41
|
-
], RunReportResultType.prototype, "ExecutionTime", void 0);
|
|
42
|
-
__decorate([
|
|
43
|
-
Field(),
|
|
44
|
-
__metadata("design:type", String)
|
|
45
|
-
], RunReportResultType.prototype, "ErrorMessage", void 0);
|
|
46
|
-
RunReportResultType = __decorate([
|
|
47
|
-
ObjectType()
|
|
48
|
-
], RunReportResultType);
|
|
49
|
-
export { RunReportResultType };
|
|
50
|
-
let CreateReportResultType = class CreateReportResultType {
|
|
51
|
-
};
|
|
52
|
-
__decorate([
|
|
53
|
-
Field(),
|
|
54
|
-
__metadata("design:type", String)
|
|
55
|
-
], CreateReportResultType.prototype, "ReportID", void 0);
|
|
56
|
-
__decorate([
|
|
57
|
-
Field(),
|
|
58
|
-
__metadata("design:type", String)
|
|
59
|
-
], CreateReportResultType.prototype, "ReportName", void 0);
|
|
60
|
-
__decorate([
|
|
61
|
-
Field(),
|
|
62
|
-
__metadata("design:type", Boolean)
|
|
63
|
-
], CreateReportResultType.prototype, "Success", void 0);
|
|
64
|
-
__decorate([
|
|
65
|
-
Field(),
|
|
66
|
-
__metadata("design:type", String)
|
|
67
|
-
], CreateReportResultType.prototype, "ErrorMessage", void 0);
|
|
68
|
-
CreateReportResultType = __decorate([
|
|
69
|
-
ObjectType()
|
|
70
|
-
], CreateReportResultType);
|
|
71
|
-
export { CreateReportResultType };
|
|
72
|
-
let ReportResolverExtended = class ReportResolverExtended extends ResolverBase {
|
|
73
|
-
async GetReportData(ReportID, context) {
|
|
74
|
-
// Check API key scope authorization for report run
|
|
75
|
-
await this.CheckAPIKeyScopeAuthorization('report:run', ReportID, context.userPayload);
|
|
76
|
-
const provider = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
77
|
-
const rp = new RunReport(provider);
|
|
78
|
-
const result = await rp.RunReport({ ReportID: ReportID });
|
|
79
|
-
return {
|
|
80
|
-
ReportID: ReportID,
|
|
81
|
-
Success: result.Success,
|
|
82
|
-
Results: JSON.stringify(result.Results),
|
|
83
|
-
RowCount: result.RowCount,
|
|
84
|
-
ExecutionTime: result.ExecutionTime,
|
|
85
|
-
ErrorMessage: result.ErrorMessage,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* This mutation will create a new report from a conversation detail ID
|
|
90
|
-
*/
|
|
91
|
-
async CreateReportFromConversationDetailID(ConversationDetailID, { dataSource, userPayload, providers }) {
|
|
92
|
-
// Check API key scope authorization for report creation (uses entity:create for Reports entity)
|
|
93
|
-
await this.CheckAPIKeyScopeAuthorization('entity:create', 'Reports', userPayload);
|
|
94
|
-
try {
|
|
95
|
-
const md = GetReadWriteProvider(providers);
|
|
96
|
-
// Resolve via the stamped payload user (GetUserFromPayload), NOT a fresh UserCache
|
|
97
|
-
// lookup by email. UserCache.Users returns the shared, unstamped instance — re-fetching
|
|
98
|
-
// by email here would silently drop any per-request API-key row-filter binding
|
|
99
|
-
// (APIKeyRowFilters / APIKeyActingContext) already stamped onto userPayload.userRecord,
|
|
100
|
-
// so a filtered key's report-creation work would run unfiltered. GetUserFromPayload
|
|
101
|
-
// prefers userPayload.userRecord and only falls back to a UserCache email lookup when
|
|
102
|
-
// no payload user is present (e.g. non-request-scoped callers), matching every other
|
|
103
|
-
// resolver's contextUser resolution.
|
|
104
|
-
const u = this.GetUserFromPayload(userPayload);
|
|
105
|
-
if (!u)
|
|
106
|
-
throw new Error('Unable to find user');
|
|
107
|
-
const cde = md.Entities.find((e) => e.Name === 'MJ: Conversation Details');
|
|
108
|
-
if (!cde)
|
|
109
|
-
throw new Error('Unable to find Conversation Details Entity metadata');
|
|
110
|
-
const cd = md.Entities.find((e) => e.Name === 'MJ: Conversations');
|
|
111
|
-
if (!cd)
|
|
112
|
-
throw new Error('Unable to find Conversations Entity metadata');
|
|
113
|
-
const sql = `SELECT
|
|
114
|
-
cd.Message, cd.ConversationID, c.DataContextID
|
|
115
|
-
FROM
|
|
116
|
-
${cde.SchemaName}.${cde.BaseView} cd
|
|
117
|
-
INNER JOIN
|
|
118
|
-
${cd.SchemaName}.${cd.BaseView} c
|
|
119
|
-
ON
|
|
120
|
-
cd.ConversationID = c.ID
|
|
121
|
-
WHERE
|
|
122
|
-
cd.ID=@ConversationDetailID`;
|
|
123
|
-
const request = new mssql.Request(dataSource);
|
|
124
|
-
request.input('ConversationDetailID', mssql.UniqueIdentifier, ConversationDetailID);
|
|
125
|
-
const result = await request.query(sql);
|
|
126
|
-
if (!result || !result.recordset || result.recordset.length === 0)
|
|
127
|
-
throw new Error('Unable to retrieve converation details');
|
|
128
|
-
const skipData = JSON.parse(result.recordset[0].Message);
|
|
129
|
-
const report = await md.GetEntityObject('MJ: Reports', u);
|
|
130
|
-
report.NewRecord();
|
|
131
|
-
// support the legacy report title as old conversation details had a reportTitle property
|
|
132
|
-
// but the new SkipData object has a title property, so favor the title property
|
|
133
|
-
const title = skipData.title ? skipData.title : skipData.reportTitle ? skipData.reportTitle : 'Untitled Report';
|
|
134
|
-
report.Name = title;
|
|
135
|
-
report.Description = skipData.userExplanation ? skipData.userExplanation : '';
|
|
136
|
-
report.ConversationID = result.recordset[0].ConversationID;
|
|
137
|
-
report.ConversationDetailID = ConversationDetailID;
|
|
138
|
-
const dc = new DataContext();
|
|
139
|
-
await dc.LoadMetadata(result.recordset[0].DataContextID, u);
|
|
140
|
-
const newDataContext = await DataContext.Clone(dc, false, u);
|
|
141
|
-
if (!newDataContext)
|
|
142
|
-
throw new Error('Error cloning data context');
|
|
143
|
-
report.DataContextID = newDataContext.ID;
|
|
144
|
-
// next, strip out the messags from the SkipData object to put them into our Report Configuration as we dont need to store that information as we have a
|
|
145
|
-
// link back to the conversation and conversation detail, skipData can be modified as it is a copy of the data doesn't affect the original source
|
|
146
|
-
skipData.messages = [];
|
|
147
|
-
report.Configuration = JSON.stringify(skipData);
|
|
148
|
-
report.SharingScope = 'None';
|
|
149
|
-
report.UserID = u.ID;
|
|
150
|
-
if (await report.Save()) {
|
|
151
|
-
return {
|
|
152
|
-
ReportID: report.ID,
|
|
153
|
-
ReportName: report.Name,
|
|
154
|
-
Success: true,
|
|
155
|
-
ErrorMessage: '',
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
return {
|
|
160
|
-
ReportID: '',
|
|
161
|
-
ReportName: '',
|
|
162
|
-
Success: false,
|
|
163
|
-
ErrorMessage: 'Unable to save new report',
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch (ex) {
|
|
168
|
-
const err = z.object({ message: z.string() }).safeParse(ex);
|
|
169
|
-
return {
|
|
170
|
-
ReportID: '',
|
|
171
|
-
ReportName: '',
|
|
172
|
-
Success: false,
|
|
173
|
-
ErrorMessage: 'Unable to create new report: ' + err.success ? err.data.message : String(ex),
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
__decorate([
|
|
179
|
-
Query(() => RunReportResultType),
|
|
180
|
-
__param(0, Arg('ReportID', () => String)),
|
|
181
|
-
__param(1, Ctx()),
|
|
182
|
-
__metadata("design:type", Function),
|
|
183
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
184
|
-
__metadata("design:returntype", Promise)
|
|
185
|
-
], ReportResolverExtended.prototype, "GetReportData", null);
|
|
186
|
-
__decorate([
|
|
187
|
-
Mutation(() => CreateReportResultType),
|
|
188
|
-
__param(0, Arg('ConversationDetailID', () => String)),
|
|
189
|
-
__param(1, Ctx()),
|
|
190
|
-
__metadata("design:type", Function),
|
|
191
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
192
|
-
__metadata("design:returntype", Promise)
|
|
193
|
-
], ReportResolverExtended.prototype, "CreateReportFromConversationDetailID", null);
|
|
194
|
-
ReportResolverExtended = __decorate([
|
|
195
|
-
Resolver(RunReportResultType)
|
|
196
|
-
], ReportResolverExtended);
|
|
197
|
-
export { ReportResolverExtended };
|
|
198
|
-
//# sourceMappingURL=ReportResolver.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ReportResolver.js","sourceRoot":"","sources":["../../src/resolvers/ReportResolver.ts"],"names":[],"mappings":"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"}
|