@memberjunction/server 6.1.0-edge.1 → 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 +45 -4
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +229 -21
- 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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -41
- 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/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/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.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +16 -1
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- 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 +13 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +30 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- 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/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 +90 -90
- 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__/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 +166 -17
- package/src/generic/ResolverBase.ts +52 -17
- package/src/index.ts +92 -42
- 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/UserFavoriteResolver.ts +1 -1
- package/src/rest/OAuthCallbackHandler.ts +1 -1
- 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 +17 -0
- package/src/services/TaskGraphActionRunner.ts +99 -0
- package/src/services/TaskGraphAgentRunner.ts +35 -1
- package/src/services/TaskGraphPromptRunner.ts +144 -0
- package/src/telephony/RingCentralTelephonyService.ts +1 -1
- package/src/telephony/TwilioTelephonyRouter.ts +1 -1
- package/src/telephony/VonageTelephonyRouter.ts +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MJServer's implementation of the task-graph `TaskActionRunner` seam.
|
|
3
|
+
*
|
|
4
|
+
* The counterpart to {@link TaskGraphAgentRunner}: the dispatcher knows *when* an action-assigned
|
|
5
|
+
* node should run and nothing about how an action executes, so execution is injected. Keeping it
|
|
6
|
+
* here rather than in `@memberjunction/task-graph` is not tidiness — the action engine depends on
|
|
7
|
+
* the entity layer the dispatcher also builds on, and importing it there would make every consumer
|
|
8
|
+
* of durable execution load the action engine as well.
|
|
9
|
+
*
|
|
10
|
+
* @module @memberjunction/server
|
|
11
|
+
*/
|
|
12
|
+
import { ActionEngineServer } from '@memberjunction/actions';
|
|
13
|
+
import { ActionParam } from '@memberjunction/actions-base';
|
|
14
|
+
import { LogError } from '@memberjunction/core';
|
|
15
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
16
|
+
import type { TaskActionRunner, TaskActionRunParams, TaskActionRunResult } from '@memberjunction/task-graph';
|
|
17
|
+
|
|
18
|
+
export class TaskGraphActionRunner implements TaskActionRunner {
|
|
19
|
+
public async RunActionForTask(params: TaskActionRunParams): Promise<TaskActionRunResult> {
|
|
20
|
+
try {
|
|
21
|
+
await ActionEngineServer.Instance.Config(false, params.ContextUser);
|
|
22
|
+
const action = ActionEngineServer.Instance.Actions.find((a) => UUIDsEqual(a.ID, params.ActionID));
|
|
23
|
+
if (!action) {
|
|
24
|
+
return { Success: false, ErrorMessage: `Action ${params.ActionID} is not in the engine's metadata.` };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const result = await ActionEngineServer.Instance.RunAction({
|
|
28
|
+
Action: action,
|
|
29
|
+
ContextUser: params.ContextUser,
|
|
30
|
+
Params: this.buildParams(params),
|
|
31
|
+
// No Filters. A durable node is work that a filter ALREADY let through — the entity
|
|
32
|
+
// action's gate ran at dispatch time, on the change context that no longer exists by
|
|
33
|
+
// the time the dispatcher picks the task up. Re-evaluating here would ask a
|
|
34
|
+
// transition question with nothing to answer it, and fail closed on every retry.
|
|
35
|
+
Filters: [],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
Success: result.Success,
|
|
40
|
+
Output: this.buildOutput(result.Params),
|
|
41
|
+
ErrorMessage: result.Success ? undefined : result.Message,
|
|
42
|
+
// The engine already wrote the log; this is the only place its id is in hand. Without
|
|
43
|
+
// carrying it out, a workflow's action step has no path back to its own execution
|
|
44
|
+
// record — the one thing anyone wants when an action misbehaves.
|
|
45
|
+
ActionLogID: result.LogEntry?.ID,
|
|
46
|
+
};
|
|
47
|
+
} catch (e) {
|
|
48
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
49
|
+
LogError(`[TaskGraphActionRunner] Task ${params.TaskID} failed: ${message}`);
|
|
50
|
+
return { Success: false, ErrorMessage: message };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Projects the action's parameters onto the flat result a workflow step is expected to produce.
|
|
56
|
+
*
|
|
57
|
+
* **Why not return `result.Params` as-is.** That is an `ActionParam[]`, and every consumer of a
|
|
58
|
+
* step's output addresses fields BY NAME: an output mapping says `{"CurrentPrice": "stockPrice"}`,
|
|
59
|
+
* and a branch condition then reads `payload.stockPrice`. Handed an array, the mapping finds no
|
|
60
|
+
* `CurrentPrice`, writes nothing, and the condition evaluates against `undefined` — which is
|
|
61
|
+
* falsy rather than erroneous, so the workflow takes the other branch and reports success. That
|
|
62
|
+
* is exactly what the Demo workflow did: step 1 completed with an output payload of `{}` and
|
|
63
|
+
* every downstream branch was skipped, with nothing anywhere reporting a problem.
|
|
64
|
+
*
|
|
65
|
+
* Only `Output` and `Both` params are included. Echoing the inputs back would let a step's own
|
|
66
|
+
* parameters masquerade as its results, so an output mapping could "succeed" by reading the
|
|
67
|
+
* value the step was given rather than the one it produced.
|
|
68
|
+
*/
|
|
69
|
+
private buildOutput(actionParams: ActionParam[]): Record<string, unknown> {
|
|
70
|
+
const output: Record<string, unknown> = {};
|
|
71
|
+
for (const p of actionParams ?? []) {
|
|
72
|
+
if (p.Type === 'Output' || p.Type === 'Both') output[p.Name] = p.Value;
|
|
73
|
+
}
|
|
74
|
+
return output;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Rebuilds the action's parameters from the task's stored payload.
|
|
79
|
+
*
|
|
80
|
+
* **The values here have been through redaction**, because `Task.InputPayload` is persistent,
|
|
81
|
+
* user-visible storage and nothing writes a raw `ActionParam[]` there. A parameter the binding
|
|
82
|
+
* marked as not-logged therefore arrives absent rather than secret — the action sees a missing
|
|
83
|
+
* value, which is the honest consequence of choosing not to persist it, and the reason durable
|
|
84
|
+
* dispatch is opt-in per binding rather than the default.
|
|
85
|
+
*
|
|
86
|
+
* Dependency outputs are merged underneath the task's own input so a node's explicit parameters
|
|
87
|
+
* always win over an upstream node that happened to emit the same key.
|
|
88
|
+
*/
|
|
89
|
+
private buildParams(params: TaskActionRunParams): ActionParam[] {
|
|
90
|
+
const merged: Record<string, unknown> = {};
|
|
91
|
+
for (const [name, value] of params.DependencyOutputs) {
|
|
92
|
+
merged[name] = value;
|
|
93
|
+
}
|
|
94
|
+
if (params.InputPayload && typeof params.InputPayload === 'object') {
|
|
95
|
+
Object.assign(merged, params.InputPayload as Record<string, unknown>);
|
|
96
|
+
}
|
|
97
|
+
return Object.entries(merged).map(([Name, Value]) => ({ Name, Value, Type: 'Input' }));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
16
16
|
import { ChatMessageRole } from '@memberjunction/ai';
|
|
17
17
|
import { LogError } from '@memberjunction/core';
|
|
18
|
-
import { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';
|
|
18
|
+
import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended } from '@memberjunction/ai-core-plus';
|
|
19
19
|
import type { TaskAgentRunner, TaskAgentRunParams, TaskAgentRunResult } from '@memberjunction/task-graph';
|
|
20
20
|
|
|
21
21
|
export class TaskGraphAgentRunner implements TaskAgentRunner {
|
|
@@ -30,6 +30,10 @@ export class TaskGraphAgentRunner implements TaskAgentRunner {
|
|
|
30
30
|
agent,
|
|
31
31
|
conversationMessages: [{ role: ChatMessageRole.user, content: this.buildPrompt(params) }],
|
|
32
32
|
contextUser: params.ContextUser,
|
|
33
|
+
// Inherited from the graph, so a graph this run goes on to emit is one hop deeper
|
|
34
|
+
// rather than starting the chain over. Without it MAX_REINVOKE_DEPTH never fires.
|
|
35
|
+
continuationDepth: params.ContinuationDepth ?? 0,
|
|
36
|
+
parentRun: await this.loadSubmittingRun(params),
|
|
33
37
|
});
|
|
34
38
|
|
|
35
39
|
const success = result?.success === true;
|
|
@@ -46,6 +50,36 @@ export class TaskGraphAgentRunner implements TaskAgentRunner {
|
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
|
|
53
|
+
/**
|
|
54
|
+
* The run that submitted this graph, as the entity `RunAgent` wants for `parentRun`.
|
|
55
|
+
*
|
|
56
|
+
* **Why this link is worth a load.** It sets `ParentRunID` on the spawned run, which turns a
|
|
57
|
+
* workflow's total cost into one indexed sum over `RootParentRunID` rather than a walk of the
|
|
58
|
+
* graph, and gives the run a "started by" trail someone can follow back. Without it every
|
|
59
|
+
* dispatched run looks like it began from nowhere.
|
|
60
|
+
*
|
|
61
|
+
* Undefined — never a throw — when there is no submitting run or it cannot be read. A graph
|
|
62
|
+
* submitted by a schedule, by MCP, or by a person genuinely has no parent run, and failing the
|
|
63
|
+
* step because its provenance is unavailable would trade real work for a bookkeeping detail.
|
|
64
|
+
*/
|
|
65
|
+
private async loadSubmittingRun(params: TaskAgentRunParams): Promise<MJAIAgentRunEntityExtended | undefined> {
|
|
66
|
+
if (!params.SubmittingAgentRunID) return undefined;
|
|
67
|
+
try {
|
|
68
|
+
const run = await params.Provider.GetEntityObject<MJAIAgentRunEntityExtended>(
|
|
69
|
+
'MJ: AI Agent Runs',
|
|
70
|
+
params.ContextUser,
|
|
71
|
+
);
|
|
72
|
+
return (await run.Load(params.SubmittingAgentRunID)) ? run : undefined;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
LogError(
|
|
75
|
+
`[TaskGraphAgentRunner] Task ${params.TaskID}: submitting run ` +
|
|
76
|
+
`${params.SubmittingAgentRunID} could not be loaded; the spawned run will have no ` +
|
|
77
|
+
`parent link. ${e instanceof Error ? e.message : String(e)}`,
|
|
78
|
+
);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
49
83
|
/**
|
|
50
84
|
* Builds the agent's instruction from the task's own input plus its dependencies' outputs.
|
|
51
85
|
*
|
|
@@ -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
|
+
}
|
|
@@ -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,
|