@memberjunction/ng-conversations 6.2.0-edge.5 → 6.2.0-edge.7
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/lib/components/attachment/image-viewer.component.d.ts +3 -1
- package/dist/lib/components/attachment/image-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/attachment/image-viewer.component.js +5 -1
- package/dist/lib/components/attachment/image-viewer.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +1 -0
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +188 -9
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +1333 -464
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-mode-picker.component.js +2 -2
- package/dist/lib/components/conversation/conversation-mode-picker.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-remote-browser-controller.d.ts +109 -0
- package/dist/lib/components/conversation/conversation-remote-browser-controller.d.ts.map +1 -0
- package/dist/lib/components/conversation/conversation-remote-browser-controller.js +298 -0
- package/dist/lib/components/conversation/conversation-remote-browser-controller.js.map +1 -0
- package/dist/lib/components/message/agent-handoff-card.component.d.ts +102 -0
- package/dist/lib/components/message/agent-handoff-card.component.d.ts.map +1 -0
- package/dist/lib/components/message/agent-handoff-card.component.js +439 -0
- package/dist/lib/components/message/agent-handoff-card.component.js.map +1 -0
- package/dist/lib/components/message/message-input.component.d.ts +126 -59
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +559 -554
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +34 -2
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +678 -520
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +13 -1
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +92 -39
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +19 -0
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +125 -13
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.js +2 -2
- package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.js +1 -1
- package/dist/lib/components/realtime/interactive-component/run-view-component-artifact-source.js.map +1 -1
- package/dist/lib/components/realtime/media/media-channel-state.d.ts +5 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/media-channel-state.js +27 -1
- package/dist/lib/components/realtime/media/media-channel-state.js.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.js +7 -2
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -1
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts +19 -2
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js +59 -10
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +22 -6
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +160 -98
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-call-failure-notice.d.ts +13 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.js +21 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.js.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +26 -3
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +226 -120
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +73 -16
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +219 -108
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +9 -12
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.js +38 -18
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +34 -5
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +8 -2
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +51 -10
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.d.ts +46 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.d.ts.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.js +64 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel-config.js.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +131 -9
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +347 -38
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +2 -1
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +9 -4
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/events/chat-events.d.ts +5 -4
- package/dist/lib/events/chat-events.d.ts.map +1 -1
- package/dist/lib/events/chat-events.js +2 -2
- package/dist/lib/events/chat-events.js.map +1 -1
- package/dist/lib/models/agent-turn.model.d.ts +11 -7
- package/dist/lib/models/agent-turn.model.d.ts.map +1 -1
- package/dist/lib/models/agent-turn.model.js.map +1 -1
- package/dist/lib/services/active-tasks.service.d.ts +5 -0
- package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
- package/dist/lib/services/active-tasks.service.js +16 -0
- package/dist/lib/services/active-tasks.service.js.map +1 -1
- package/dist/lib/services/agent-state.service.d.ts.map +1 -1
- package/dist/lib/services/agent-state.service.js +7 -1
- package/dist/lib/services/agent-state.service.js.map +1 -1
- package/dist/lib/services/artifact-permission.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-permission.service.js +2 -0
- package/dist/lib/services/artifact-permission.service.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +1 -0
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/collection-permission.service.d.ts.map +1 -1
- package/dist/lib/services/collection-permission.service.js +1 -0
- package/dist/lib/services/collection-permission.service.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +38 -16
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +57 -71
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.d.ts +5 -0
- package/dist/lib/services/conversation-detail-window.store.d.ts.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.js +52 -5
- package/dist/lib/services/conversation-detail-window.store.js.map +1 -1
- package/dist/lib/services/realtime-session-review.service.d.ts +2 -1
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session-review.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +2 -1
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/search.service.d.ts.map +1 -1
- package/dist/lib/services/search.service.js +1 -0
- package/dist/lib/services/search.service.js.map +1 -1
- package/dist/lib/utils/agent-failure-display.d.ts +14 -0
- package/dist/lib/utils/agent-failure-display.d.ts.map +1 -0
- package/dist/lib/utils/agent-failure-display.js +26 -0
- package/dist/lib/utils/agent-failure-display.js.map +1 -0
- package/dist/lib/utils/agent-handoff-map.d.ts +52 -0
- package/dist/lib/utils/agent-handoff-map.d.ts.map +1 -0
- package/dist/lib/utils/agent-handoff-map.js +270 -0
- package/dist/lib/utils/agent-handoff-map.js.map +1 -0
- package/dist/lib/utils/agent-turn-routing.d.ts +62 -7
- package/dist/lib/utils/agent-turn-routing.d.ts.map +1 -1
- package/dist/lib/utils/agent-turn-routing.js +147 -6
- package/dist/lib/utils/agent-turn-routing.js.map +1 -1
- package/dist/lib/utils/decision-routing.d.ts +17 -9
- package/dist/lib/utils/decision-routing.d.ts.map +1 -1
- package/dist/lib/utils/decision-routing.js +23 -14
- package/dist/lib/utils/decision-routing.js.map +1 -1
- package/dist/lib/utils/message-list-order.d.ts +13 -0
- package/dist/lib/utils/message-list-order.d.ts.map +1 -0
- package/dist/lib/utils/message-list-order.js +31 -0
- package/dist/lib/utils/message-list-order.js.map +1 -0
- package/dist/lib/utils/pin-state.d.ts +11 -0
- package/dist/lib/utils/pin-state.d.ts.map +1 -0
- package/dist/lib/utils/pin-state.js +9 -0
- package/dist/lib/utils/pin-state.js.map +1 -0
- package/dist/lib/utils/thread-replies.d.ts +34 -0
- package/dist/lib/utils/thread-replies.d.ts.map +1 -0
- package/dist/lib/utils/thread-replies.js +49 -0
- package/dist/lib/utils/thread-replies.js.map +1 -0
- package/dist/public-api.d.ts +6 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +36 -36
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { LogError, RunView } from '@memberjunction/core';
|
|
2
|
+
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
3
|
+
import { CleanAndParseJSON, EscapeSQLString, IndexByUUID, NormalizeUUID, UUIDsEqual, } from '@memberjunction/global';
|
|
4
|
+
export const HANDOFF_STEP_FIELDS = [
|
|
5
|
+
'ID',
|
|
6
|
+
'AgentRunID',
|
|
7
|
+
'StepNumber',
|
|
8
|
+
'StepName',
|
|
9
|
+
'StepType',
|
|
10
|
+
'Status',
|
|
11
|
+
'TargetID',
|
|
12
|
+
'InputData',
|
|
13
|
+
'OutputData',
|
|
14
|
+
'ErrorMessage',
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Loads `MJ: AI Agent Run Steps` rows with `StepType = 'Handoff'` for the given run IDs
|
|
18
|
+
* in a single `RunView` query (`ResultType: 'simple'`, explicit `Fields`).
|
|
19
|
+
*/
|
|
20
|
+
export async function LoadHandoffStepsForRuns(provider, runIds) {
|
|
21
|
+
const uniqueIds = Array.from(new Set(runIds.map(id => id?.trim()).filter((id) => !!id)));
|
|
22
|
+
if (uniqueIds.length === 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const rv = RunView.FromMetadataProvider(provider);
|
|
27
|
+
const quotedIds = uniqueIds.map(id => `'${EscapeSQLString(id)}'`).join(',');
|
|
28
|
+
const result = await rv.RunView({
|
|
29
|
+
EntityName: 'MJ: AI Agent Run Steps',
|
|
30
|
+
ExtraFilter: `StepType = 'Handoff' AND AgentRunID IN (${quotedIds})`,
|
|
31
|
+
Fields: HANDOFF_STEP_FIELDS,
|
|
32
|
+
OrderBy: 'StepNumber ASC',
|
|
33
|
+
ResultType: 'simple',
|
|
34
|
+
});
|
|
35
|
+
if (!result?.Success || !result.Results) {
|
|
36
|
+
LogError(`LoadHandoffStepsForRuns failed: ${result?.ErrorMessage ?? 'Unknown RunView error'}`);
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return result.Results;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
LogError('LoadHandoffStepsForRuns threw an error:', undefined, error);
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function parseJsonSafe(raw) {
|
|
47
|
+
if (!raw || !raw.trim()) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(raw);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return CleanAndParseJSON(raw, false);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function resolveParticipant(agentId, fallbackName, agents) {
|
|
58
|
+
const agentsById = IndexByUUID(agents);
|
|
59
|
+
const match = agentId
|
|
60
|
+
? agentsById.Get(agentId)
|
|
61
|
+
: fallbackName
|
|
62
|
+
? agents.find(a => a.Name?.trim().toLowerCase() === fallbackName.trim().toLowerCase())
|
|
63
|
+
: undefined;
|
|
64
|
+
if (match) {
|
|
65
|
+
return {
|
|
66
|
+
ID: match.ID,
|
|
67
|
+
Name: match.Name || fallbackName || 'Agent',
|
|
68
|
+
AvatarUrl: match.LogoURL || null,
|
|
69
|
+
IconClass: match.IconClass || null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
ID: agentId ?? null,
|
|
74
|
+
Name: fallbackName?.trim() || 'Agent',
|
|
75
|
+
AvatarUrl: null,
|
|
76
|
+
IconClass: null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Looks up a `MessageHandoffInfo` in a detail-ID map using case-insensitive UUID matching.
|
|
81
|
+
*/
|
|
82
|
+
export function LookupHandoffByDetailId(map, detailId) {
|
|
83
|
+
if (!map || !detailId) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const direct = map.get(detailId) ?? map.get(NormalizeUUID(detailId));
|
|
87
|
+
if (direct) {
|
|
88
|
+
return direct;
|
|
89
|
+
}
|
|
90
|
+
for (const [key, value] of map.entries()) {
|
|
91
|
+
if (UUIDsEqual(key, detailId)) {
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Finds the target detail row in `messages` for a handoff originating from `sourceDetailId`.
|
|
99
|
+
*/
|
|
100
|
+
export function FindHandoffTargetDetail(sourceDetailId, targetAgentId, messages) {
|
|
101
|
+
return messages.find(m => m.Role === 'AI' &&
|
|
102
|
+
!UUIDsEqual(m.ID, sourceDetailId) &&
|
|
103
|
+
UUIDsEqual(m.ParentID, sourceDetailId) &&
|
|
104
|
+
(!targetAgentId || !m.AgentID || UUIDsEqual(m.AgentID, targetAgentId)));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Computes the effective `Status` of a handoff given the current loaded `messages`.
|
|
108
|
+
*/
|
|
109
|
+
export function DeriveHandoffStatus(baseStatus, sourceDetailId, targetAgentId, targetDetailId, messages) {
|
|
110
|
+
if (baseStatus === 'Failed') {
|
|
111
|
+
return 'Failed';
|
|
112
|
+
}
|
|
113
|
+
const targetDetail = targetDetailId
|
|
114
|
+
? messages.find(m => UUIDsEqual(m.ID, targetDetailId)) ??
|
|
115
|
+
FindHandoffTargetDetail(sourceDetailId, targetAgentId, messages)
|
|
116
|
+
: FindHandoffTargetDetail(sourceDetailId, targetAgentId, messages);
|
|
117
|
+
if (!targetDetail || targetDetail.Status === 'In-Progress') {
|
|
118
|
+
return 'Transferring';
|
|
119
|
+
}
|
|
120
|
+
return 'Transferred';
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Builds the `HandoffsByDetailId` map from loaded messages, agent runs, and `Handoff` run steps.
|
|
124
|
+
*/
|
|
125
|
+
export function BuildHandoffsByDetailId(params) {
|
|
126
|
+
const { Messages: messages, AgentRunsByDetailId: agentRunsByDetailId, HandoffSteps: handoffSteps, Agents: agents, Presets: presets = [], } = params;
|
|
127
|
+
const result = new Map();
|
|
128
|
+
if (handoffSteps.length === 0 || agentRunsByDetailId.size === 0) {
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
const stepsByRunKey = new Map();
|
|
132
|
+
for (const step of handoffSteps) {
|
|
133
|
+
if (!step.AgentRunID)
|
|
134
|
+
continue;
|
|
135
|
+
if (step.StepType && step.StepType !== 'Handoff')
|
|
136
|
+
continue;
|
|
137
|
+
const key = NormalizeUUID(step.AgentRunID);
|
|
138
|
+
const list = stepsByRunKey.get(key) ?? [];
|
|
139
|
+
list.push(step);
|
|
140
|
+
stepsByRunKey.set(key, list);
|
|
141
|
+
}
|
|
142
|
+
for (const list of stepsByRunKey.values()) {
|
|
143
|
+
list.sort((a, b) => (a.StepNumber ?? 0) - (b.StepNumber ?? 0));
|
|
144
|
+
}
|
|
145
|
+
const messagesById = IndexByUUID(messages);
|
|
146
|
+
const presetsById = IndexByUUID(presets);
|
|
147
|
+
const defaultPresetsByAgentKey = new Map();
|
|
148
|
+
for (const p of presets) {
|
|
149
|
+
if (p.Status === 'Active' && p.IsDefault && p.AgentID) {
|
|
150
|
+
const key = NormalizeUUID(p.AgentID);
|
|
151
|
+
if (!defaultPresetsByAgentKey.has(key)) {
|
|
152
|
+
defaultPresetsByAgentKey.set(key, p);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const [detailId, agentRun] of agentRunsByDetailId.entries()) {
|
|
157
|
+
const runSteps = stepsByRunKey.get(NormalizeUUID(agentRun.ID));
|
|
158
|
+
if (!runSteps || runSteps.length === 0) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const completedSteps = runSteps.filter(s => s.Status === 'Completed');
|
|
162
|
+
const chosenStep = completedSteps.length > 0
|
|
163
|
+
? completedSteps[completedSteps.length - 1]
|
|
164
|
+
: runSteps[runSteps.length - 1];
|
|
165
|
+
const isFailed = chosenStep.Status === 'Failed' && completedSteps.length === 0;
|
|
166
|
+
const input = parseJsonSafe(chosenStep.InputData);
|
|
167
|
+
const output = parseJsonSafe(chosenStep.OutputData);
|
|
168
|
+
const sourceDetail = messagesById.Get(detailId);
|
|
169
|
+
const sourceAgentId = sourceDetail?.AgentID || agentRun.AgentID || null;
|
|
170
|
+
const sourceFallbackName = sourceDetail?.Agent || agentRun.Agent || 'Sage';
|
|
171
|
+
const sourceParticipant = resolveParticipant(sourceAgentId, sourceFallbackName, agents);
|
|
172
|
+
const targetAgentId = chosenStep.TargetID || output?.HandoffToAgentID || null;
|
|
173
|
+
const targetFallbackName = output?.TargetAgentName ||
|
|
174
|
+
output?.AgentName ||
|
|
175
|
+
input?.agentName ||
|
|
176
|
+
chosenStep.StepName?.replace(/^Handoff:\s*/i, '').trim() ||
|
|
177
|
+
'Agent';
|
|
178
|
+
const targetParticipant = resolveParticipant(targetAgentId, targetFallbackName, agents);
|
|
179
|
+
const explicitTargetDetailId = output?.TargetConversationDetailID ?? null;
|
|
180
|
+
const targetDetail = isFailed
|
|
181
|
+
? undefined
|
|
182
|
+
: (explicitTargetDetailId ? messagesById.Get(explicitTargetDetailId) : undefined) ??
|
|
183
|
+
FindHandoffTargetDetail(detailId, targetParticipant.ID, messages);
|
|
184
|
+
const resolvedTargetDetailId = targetDetail?.ID ?? explicitTargetDetailId ?? null;
|
|
185
|
+
const status = isFailed
|
|
186
|
+
? 'Failed'
|
|
187
|
+
: DeriveHandoffStatus('Transferred', detailId, targetParticipant.ID, resolvedTargetDetailId, messages);
|
|
188
|
+
let presetName = null;
|
|
189
|
+
if (!isFailed) {
|
|
190
|
+
if (output?.PresetName?.trim()) {
|
|
191
|
+
presetName = output.PresetName.trim();
|
|
192
|
+
}
|
|
193
|
+
else if (output?.PresetID) {
|
|
194
|
+
const matchedPreset = presetsById.Get(output.PresetID);
|
|
195
|
+
presetName = matchedPreset?.DisplayName || matchedPreset?.Name || null;
|
|
196
|
+
}
|
|
197
|
+
else if (targetParticipant.ID) {
|
|
198
|
+
const defaultPreset = defaultPresetsByAgentKey.get(NormalizeUUID(targetParticipant.ID)) ??
|
|
199
|
+
AIEngineBase.Instance.GetDefaultAgentConfigurationPreset(targetParticipant.ID);
|
|
200
|
+
presetName = defaultPreset?.DisplayName || defaultPreset?.Name || null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const brief = output?.Brief ?? input?.brief ?? null;
|
|
204
|
+
const reason = output?.Reason ?? input?.reason ?? null;
|
|
205
|
+
const failureReason = isFailed
|
|
206
|
+
? output?.RefusalReason || chosenStep.ErrorMessage || 'Transfer could not complete'
|
|
207
|
+
: null;
|
|
208
|
+
let hopChain = null;
|
|
209
|
+
if (Array.isArray(output?.Chain) && output.Chain.length >= 2) {
|
|
210
|
+
const chainParticipants = output.Chain.map(hopAgentId => resolveParticipant(hopAgentId, null, agents));
|
|
211
|
+
chainParticipants.push(targetParticipant);
|
|
212
|
+
hopChain = chainParticipants;
|
|
213
|
+
}
|
|
214
|
+
result.set(detailId, {
|
|
215
|
+
SourceAgent: sourceParticipant,
|
|
216
|
+
TargetAgent: targetParticipant,
|
|
217
|
+
PresetName: presetName,
|
|
218
|
+
Brief: brief,
|
|
219
|
+
Reason: reason,
|
|
220
|
+
Status: status,
|
|
221
|
+
FailureReason: failureReason,
|
|
222
|
+
HopChain: hopChain,
|
|
223
|
+
TargetDetailId: resolvedTargetDetailId,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Resolves `MessageHandedOffFromInfo` for a target message row when its parent row handed off to it.
|
|
230
|
+
*/
|
|
231
|
+
export function ResolveHandedOffFromForMessage(message, handoffsByDetailId) {
|
|
232
|
+
if (!handoffsByDetailId || handoffsByDetailId.size === 0 || message.Role !== 'AI') {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
if (message.ParentID) {
|
|
236
|
+
const parentHandoff = LookupHandoffByDetailId(handoffsByDetailId, message.ParentID);
|
|
237
|
+
if (parentHandoff &&
|
|
238
|
+
parentHandoff.Status !== 'Failed' &&
|
|
239
|
+
(!parentHandoff.TargetAgent.ID ||
|
|
240
|
+
!message.AgentID ||
|
|
241
|
+
UUIDsEqual(parentHandoff.TargetAgent.ID, message.AgentID))) {
|
|
242
|
+
return {
|
|
243
|
+
SourceAgent: parentHandoff.SourceAgent,
|
|
244
|
+
TargetAgent: parentHandoff.TargetAgent,
|
|
245
|
+
PresetName: parentHandoff.PresetName ?? null,
|
|
246
|
+
Brief: parentHandoff.Brief ?? null,
|
|
247
|
+
Reason: parentHandoff.Reason ?? null,
|
|
248
|
+
HopChain: parentHandoff.HopChain ?? null,
|
|
249
|
+
SourceDetailId: message.ParentID,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const [sourceDetailId, handoff] of handoffsByDetailId.entries()) {
|
|
254
|
+
if (handoff.Status !== 'Failed' &&
|
|
255
|
+
handoff.TargetDetailId &&
|
|
256
|
+
UUIDsEqual(handoff.TargetDetailId, message.ID)) {
|
|
257
|
+
return {
|
|
258
|
+
SourceAgent: handoff.SourceAgent,
|
|
259
|
+
TargetAgent: handoff.TargetAgent,
|
|
260
|
+
PresetName: handoff.PresetName ?? null,
|
|
261
|
+
Brief: handoff.Brief ?? null,
|
|
262
|
+
Reason: handoff.Reason ?? null,
|
|
263
|
+
HopChain: handoff.HopChain ?? null,
|
|
264
|
+
SourceDetailId: sourceDetailId,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=agent-handoff-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-handoff-map.js","sourceRoot":"","sources":["../../../src/lib/utils/agent-handoff-map.ts"],"names":[],"mappings":"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","sourcesContent":["import { IMetadataProvider, LogError, RunView } from '@memberjunction/core';\nimport {\n MJConversationDetailEntity,\n MJAIAgentConfigurationEntity,\n} from '@memberjunction/core-entities';\nimport {\n MJAIAgentEntityExtended,\n MJAIAgentRunEntityExtended,\n} from '@memberjunction/ai-core-plus';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport {\n CleanAndParseJSON,\n EscapeSQLString,\n IndexByUUID,\n NormalizeUUID,\n UUIDsEqual,\n} from '@memberjunction/global';\nimport {\n AgentHandoffParticipant,\n AgentHandoffStatus,\n MessageHandedOffFromInfo,\n MessageHandoffInfo,\n} from '../components/message/agent-handoff-card.component';\n\n/**\n * Simple row shape projected from `MJ: AI Agent Run Steps` for `StepType = 'Handoff'`.\n */\nexport interface HandoffRunStepRow {\n ID: string;\n AgentRunID: string;\n StepNumber?: number | null;\n StepName?: string | null;\n StepType?: string | null;\n Status?: string | null;\n TargetID?: string | null;\n InputData?: string | null;\n OutputData?: string | null;\n ErrorMessage?: string | null;\n}\n\ninterface ParsedHandoffInput {\n agentName?: string;\n brief?: string;\n reason?: string;\n}\n\ninterface ParsedHandoffOutput {\n HandoffToAgentID?: string;\n TargetAgentName?: string;\n AgentName?: string;\n TargetConversationDetailID?: string | null;\n PresetID?: string | null;\n PresetName?: string | null;\n PresetSource?: string | null;\n Brief?: string;\n Reason?: string;\n RefusalReason?: string;\n Hop?: number;\n Chain?: string[];\n}\n\nexport const HANDOFF_STEP_FIELDS: string[] = [\n 'ID',\n 'AgentRunID',\n 'StepNumber',\n 'StepName',\n 'StepType',\n 'Status',\n 'TargetID',\n 'InputData',\n 'OutputData',\n 'ErrorMessage',\n];\n\n/**\n * Loads `MJ: AI Agent Run Steps` rows with `StepType = 'Handoff'` for the given run IDs\n * in a single `RunView` query (`ResultType: 'simple'`, explicit `Fields`).\n */\nexport async function LoadHandoffStepsForRuns(\n provider: IMetadataProvider,\n runIds: readonly string[]\n): Promise<HandoffRunStepRow[]> {\n const uniqueIds = Array.from(\n new Set(runIds.map(id => id?.trim()).filter((id): id is string => !!id))\n );\n if (uniqueIds.length === 0) {\n return [];\n }\n try {\n const rv = RunView.FromMetadataProvider(provider);\n const quotedIds = uniqueIds.map(id => `'${EscapeSQLString(id)}'`).join(',');\n const result = await rv.RunView<HandoffRunStepRow>({\n EntityName: 'MJ: AI Agent Run Steps',\n ExtraFilter: `StepType = 'Handoff' AND AgentRunID IN (${quotedIds})`,\n Fields: HANDOFF_STEP_FIELDS,\n OrderBy: 'StepNumber ASC',\n ResultType: 'simple',\n });\n if (!result?.Success || !result.Results) {\n LogError(`LoadHandoffStepsForRuns failed: ${result?.ErrorMessage ?? 'Unknown RunView error'}`);\n return [];\n }\n return result.Results;\n } catch (error) {\n LogError('LoadHandoffStepsForRuns threw an error:', undefined, error);\n return [];\n }\n}\n\nfunction parseJsonSafe<T>(raw: string | null | undefined): T | null {\n if (!raw || !raw.trim()) {\n return null;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return CleanAndParseJSON<T>(raw, false);\n }\n}\n\nfunction resolveParticipant(\n agentId: string | null | undefined,\n fallbackName: string | null | undefined,\n agents: readonly MJAIAgentEntityExtended[]\n): AgentHandoffParticipant {\n const agentsById = IndexByUUID(agents);\n const match = agentId\n ? agentsById.Get(agentId)\n : fallbackName\n ? agents.find(a => a.Name?.trim().toLowerCase() === fallbackName.trim().toLowerCase())\n : undefined;\n\n if (match) {\n return {\n ID: match.ID,\n Name: match.Name || fallbackName || 'Agent',\n AvatarUrl: match.LogoURL || null,\n IconClass: match.IconClass || null,\n };\n }\n return {\n ID: agentId ?? null,\n Name: fallbackName?.trim() || 'Agent',\n AvatarUrl: null,\n IconClass: null,\n };\n}\n\n/**\n * Looks up a `MessageHandoffInfo` in a detail-ID map using case-insensitive UUID matching.\n */\nexport function LookupHandoffByDetailId(\n map: ReadonlyMap<string, MessageHandoffInfo> | null | undefined,\n detailId: string | null | undefined\n): MessageHandoffInfo | null {\n if (!map || !detailId) {\n return null;\n }\n const direct = map.get(detailId) ?? map.get(NormalizeUUID(detailId));\n if (direct) {\n return direct;\n }\n for (const [key, value] of map.entries()) {\n if (UUIDsEqual(key, detailId)) {\n return value;\n }\n }\n return null;\n}\n\n/**\n * Finds the target detail row in `messages` for a handoff originating from `sourceDetailId`.\n */\nexport function FindHandoffTargetDetail(\n sourceDetailId: string,\n targetAgentId: string | null | undefined,\n messages: readonly MJConversationDetailEntity[]\n): MJConversationDetailEntity | undefined {\n return messages.find(\n m =>\n m.Role === 'AI' &&\n !UUIDsEqual(m.ID, sourceDetailId) &&\n UUIDsEqual(m.ParentID, sourceDetailId) &&\n (!targetAgentId || !m.AgentID || UUIDsEqual(m.AgentID, targetAgentId))\n );\n}\n\n/**\n * Computes the effective `Status` of a handoff given the current loaded `messages`.\n */\nexport function DeriveHandoffStatus(\n baseStatus: AgentHandoffStatus,\n sourceDetailId: string,\n targetAgentId: string | null | undefined,\n targetDetailId: string | null | undefined,\n messages: readonly MJConversationDetailEntity[]\n): AgentHandoffStatus {\n if (baseStatus === 'Failed') {\n return 'Failed';\n }\n const targetDetail = targetDetailId\n ? messages.find(m => UUIDsEqual(m.ID, targetDetailId)) ??\n FindHandoffTargetDetail(sourceDetailId, targetAgentId, messages)\n : FindHandoffTargetDetail(sourceDetailId, targetAgentId, messages);\n if (!targetDetail || targetDetail.Status === 'In-Progress') {\n return 'Transferring';\n }\n return 'Transferred';\n}\n\n/**\n * Builds the `HandoffsByDetailId` map from loaded messages, agent runs, and `Handoff` run steps.\n */\nexport function BuildHandoffsByDetailId(params: {\n Messages: readonly MJConversationDetailEntity[];\n AgentRunsByDetailId: ReadonlyMap<string, MJAIAgentRunEntityExtended>;\n HandoffSteps: readonly HandoffRunStepRow[];\n Agents: readonly MJAIAgentEntityExtended[];\n Presets?: readonly MJAIAgentConfigurationEntity[];\n}): Map<string, MessageHandoffInfo> {\n const {\n Messages: messages,\n AgentRunsByDetailId: agentRunsByDetailId,\n HandoffSteps: handoffSteps,\n Agents: agents,\n Presets: presets = [],\n } = params;\n\n const result = new Map<string, MessageHandoffInfo>();\n if (handoffSteps.length === 0 || agentRunsByDetailId.size === 0) {\n return result;\n }\n\n const stepsByRunKey = new Map<string, HandoffRunStepRow[]>();\n for (const step of handoffSteps) {\n if (!step.AgentRunID) continue;\n if (step.StepType && step.StepType !== 'Handoff') continue;\n const key = NormalizeUUID(step.AgentRunID);\n const list = stepsByRunKey.get(key) ?? [];\n list.push(step);\n stepsByRunKey.set(key, list);\n }\n\n for (const list of stepsByRunKey.values()) {\n list.sort((a, b) => (a.StepNumber ?? 0) - (b.StepNumber ?? 0));\n }\n\n const messagesById = IndexByUUID(messages);\n const presetsById = IndexByUUID(presets);\n const defaultPresetsByAgentKey = new Map<string, MJAIAgentConfigurationEntity>();\n for (const p of presets) {\n if (p.Status === 'Active' && p.IsDefault && p.AgentID) {\n const key = NormalizeUUID(p.AgentID);\n if (!defaultPresetsByAgentKey.has(key)) {\n defaultPresetsByAgentKey.set(key, p);\n }\n }\n }\n\n for (const [detailId, agentRun] of agentRunsByDetailId.entries()) {\n const runSteps = stepsByRunKey.get(NormalizeUUID(agentRun.ID));\n if (!runSteps || runSteps.length === 0) {\n continue;\n }\n\n const completedSteps = runSteps.filter(s => s.Status === 'Completed');\n const chosenStep =\n completedSteps.length > 0\n ? completedSteps[completedSteps.length - 1]\n : runSteps[runSteps.length - 1];\n\n const isFailed = chosenStep.Status === 'Failed' && completedSteps.length === 0;\n const input = parseJsonSafe<ParsedHandoffInput>(chosenStep.InputData);\n const output = parseJsonSafe<ParsedHandoffOutput>(chosenStep.OutputData);\n\n const sourceDetail = messagesById.Get(detailId);\n const sourceAgentId = sourceDetail?.AgentID || agentRun.AgentID || null;\n const sourceFallbackName = sourceDetail?.Agent || agentRun.Agent || 'Sage';\n const sourceParticipant = resolveParticipant(sourceAgentId, sourceFallbackName, agents);\n\n const targetAgentId =\n chosenStep.TargetID || output?.HandoffToAgentID || null;\n const targetFallbackName =\n output?.TargetAgentName ||\n output?.AgentName ||\n input?.agentName ||\n chosenStep.StepName?.replace(/^Handoff:\\s*/i, '').trim() ||\n 'Agent';\n const targetParticipant = resolveParticipant(targetAgentId, targetFallbackName, agents);\n\n const explicitTargetDetailId = output?.TargetConversationDetailID ?? null;\n const targetDetail = isFailed\n ? undefined\n : (explicitTargetDetailId ? messagesById.Get(explicitTargetDetailId) : undefined) ??\n FindHandoffTargetDetail(detailId, targetParticipant.ID, messages);\n\n const resolvedTargetDetailId = targetDetail?.ID ?? explicitTargetDetailId ?? null;\n\n const status: AgentHandoffStatus = isFailed\n ? 'Failed'\n : DeriveHandoffStatus(\n 'Transferred',\n detailId,\n targetParticipant.ID,\n resolvedTargetDetailId,\n messages\n );\n\n let presetName: string | null = null;\n if (!isFailed) {\n if (output?.PresetName?.trim()) {\n presetName = output.PresetName.trim();\n } else if (output?.PresetID) {\n const matchedPreset = presetsById.Get(output.PresetID);\n presetName = matchedPreset?.DisplayName || matchedPreset?.Name || null;\n } else if (targetParticipant.ID) {\n const defaultPreset =\n defaultPresetsByAgentKey.get(NormalizeUUID(targetParticipant.ID)) ??\n AIEngineBase.Instance.GetDefaultAgentConfigurationPreset(targetParticipant.ID);\n presetName = defaultPreset?.DisplayName || defaultPreset?.Name || null;\n }\n }\n\n const brief = output?.Brief ?? input?.brief ?? null;\n const reason = output?.Reason ?? input?.reason ?? null;\n const failureReason = isFailed\n ? output?.RefusalReason || chosenStep.ErrorMessage || 'Transfer could not complete'\n : null;\n\n let hopChain: AgentHandoffParticipant[] | null = null;\n if (Array.isArray(output?.Chain) && output.Chain.length >= 2) {\n const chainParticipants = output.Chain.map(hopAgentId =>\n resolveParticipant(hopAgentId, null, agents)\n );\n chainParticipants.push(targetParticipant);\n hopChain = chainParticipants;\n }\n\n result.set(detailId, {\n SourceAgent: sourceParticipant,\n TargetAgent: targetParticipant,\n PresetName: presetName,\n Brief: brief,\n Reason: reason,\n Status: status,\n FailureReason: failureReason,\n HopChain: hopChain,\n TargetDetailId: resolvedTargetDetailId,\n });\n }\n\n return result;\n}\n\n/**\n * Resolves `MessageHandedOffFromInfo` for a target message row when its parent row handed off to it.\n */\nexport function ResolveHandedOffFromForMessage(\n message: MJConversationDetailEntity,\n handoffsByDetailId: ReadonlyMap<string, MessageHandoffInfo> | null | undefined\n): MessageHandedOffFromInfo | null {\n if (!handoffsByDetailId || handoffsByDetailId.size === 0 || message.Role !== 'AI') {\n return null;\n }\n\n if (message.ParentID) {\n const parentHandoff = LookupHandoffByDetailId(handoffsByDetailId, message.ParentID);\n if (\n parentHandoff &&\n parentHandoff.Status !== 'Failed' &&\n (!parentHandoff.TargetAgent.ID ||\n !message.AgentID ||\n UUIDsEqual(parentHandoff.TargetAgent.ID, message.AgentID))\n ) {\n return {\n SourceAgent: parentHandoff.SourceAgent,\n TargetAgent: parentHandoff.TargetAgent,\n PresetName: parentHandoff.PresetName ?? null,\n Brief: parentHandoff.Brief ?? null,\n Reason: parentHandoff.Reason ?? null,\n HopChain: parentHandoff.HopChain ?? null,\n SourceDetailId: message.ParentID,\n };\n }\n }\n\n for (const [sourceDetailId, handoff] of handoffsByDetailId.entries()) {\n if (\n handoff.Status !== 'Failed' &&\n handoff.TargetDetailId &&\n UUIDsEqual(handoff.TargetDetailId, message.ID)\n ) {\n return {\n SourceAgent: handoff.SourceAgent,\n TargetAgent: handoff.TargetAgent,\n PresetName: handoff.PresetName ?? null,\n Brief: handoff.Brief ?? null,\n Reason: handoff.Reason ?? null,\n HopChain: handoff.HopChain ?? null,\n SourceDetailId: sourceDetailId,\n };\n }\n }\n\n return null;\n}\n"]}
|
|
@@ -10,13 +10,22 @@ import type { AgentReplyMode, AgentTurnTarget } from '../models/agent-turn.model
|
|
|
10
10
|
export interface AgentTurnCandidates {
|
|
11
11
|
/** Agents the message tags, in the order they appear. */
|
|
12
12
|
MentionedAgentIds: readonly string[];
|
|
13
|
+
/** People the message tags (`@Person`). Addressing a person with no agent tag starts no agent turn. */
|
|
14
|
+
MentionedUserIds?: readonly string[];
|
|
15
|
+
/** The agent of the message this one replies to (`ParentID` -> `Role === 'AI'`), when set. */
|
|
16
|
+
ReplyParentAgentId?: string | null;
|
|
17
|
+
/** True when this message replies (`ParentID`) to another person's message (`Role === 'User'`). */
|
|
18
|
+
ReplyParentIsHuman?: boolean;
|
|
13
19
|
/** The last agent that answered in the conversation, other than the conversation manager. */
|
|
14
20
|
ContinuityAgentId: string | null;
|
|
15
21
|
/**
|
|
16
22
|
* True when a routing decision put `ContinuityAgentId` there in place of the last agent that
|
|
17
|
-
* answered. The turn is then labelled `DecisionRouted`
|
|
23
|
+
* answered (or confirmed it in a multi-person chat). The turn is then labelled `DecisionRouted`
|
|
24
|
+
* instead of `Continuity`.
|
|
18
25
|
*/
|
|
19
26
|
ContinuityDecisionRouted?: boolean;
|
|
27
|
+
/** True when a routing decision in a multi-person chat concluded the message is addressed to a person. */
|
|
28
|
+
DecisionAddressedToHuman?: boolean;
|
|
20
29
|
/** The agent pinned on the conversation. */
|
|
21
30
|
ConversationDefaultAgentId: string | null;
|
|
22
31
|
/** The host's `DefaultAgentId` input. */
|
|
@@ -31,23 +40,32 @@ export interface AgentTurnRules {
|
|
|
31
40
|
/** The agents that may answer. Null allows every agent. */
|
|
32
41
|
AllowedAgentIDs: readonly string[] | null;
|
|
33
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* True when a message with no allowed `@Agent` mention is intentionally addressed to a person
|
|
45
|
+
* rather than an agent: it tags a person (`@Person`), replies (`ParentID`) to another person's
|
|
46
|
+
* message, or was judged human-addressed by the routing decision.
|
|
47
|
+
*/
|
|
48
|
+
export declare function IsAddressedToHuman(candidates: AgentTurnCandidates): boolean;
|
|
34
49
|
/**
|
|
35
50
|
* Picks the agent and route for one message, or null when no turn starts.
|
|
36
51
|
*
|
|
37
|
-
* The order is MJ's: the first tagged agent the host allows,
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
52
|
+
* The order is MJ's: the first tagged agent the host allows, a direct thread reply (`ParentID`) to
|
|
53
|
+
* a runnable agent, a continuity candidate that a routing decision chose (`DecisionRouted`), and
|
|
54
|
+
* then (under `Always` only) the last agent that answered, the conversation's pinned agent, the
|
|
55
|
+
* host's default agent, and the conversation manager. A message that tags a person, replies to
|
|
56
|
+
* another person's message, or receives a `NoAgent` routing decision with no allowed `@Agent` tag
|
|
57
|
+
* starts no turn. A candidate the host doesn't allow is skipped.
|
|
41
58
|
*
|
|
42
59
|
* A continuity or default candidate that `isKnownAgent` doesn't recognise falls back to the
|
|
43
60
|
* conversation manager, as MJ always has, rather than to the next route. A tagged agent is taken
|
|
44
|
-
* as tagged
|
|
61
|
+
* as tagged; a `ThreadReply` target must pass `isRunnableAgent`.
|
|
45
62
|
*
|
|
46
63
|
* @param candidates The agent each route would use.
|
|
47
64
|
* @param rules The host's reply mode and allowed list.
|
|
48
65
|
* @param isKnownAgent True when the agent exists in the client's agent catalog.
|
|
66
|
+
* @param isRunnableAgent True when the current user can run the agent directly (defaults to `isKnownAgent`).
|
|
49
67
|
*/
|
|
50
|
-
export declare function ResolveAgentTurn(candidates: AgentTurnCandidates, rules: AgentTurnRules, isKnownAgent: (agentId: string) => boolean): AgentTurnTarget | null;
|
|
68
|
+
export declare function ResolveAgentTurn(candidates: AgentTurnCandidates, rules: AgentTurnRules, isKnownAgent: (agentId: string) => boolean, isRunnableAgent?: (agentId: string) => boolean): AgentTurnTarget | null;
|
|
51
69
|
/** A task-graph step that runs an agent, as the conversation manager emits it. */
|
|
52
70
|
interface AgentTaskStep {
|
|
53
71
|
kind: string;
|
|
@@ -66,5 +84,42 @@ interface AgentTaskStep {
|
|
|
66
84
|
* @returns The disallowed names, each once, in the order they appear. Empty when all are allowed.
|
|
67
85
|
*/
|
|
68
86
|
export declare function FindDisallowedTaskGraphAgents(steps: ReadonlyArray<AgentTaskStep>, allowedAgentIDs: readonly string[] | null, resolveAgentId: (agentName: string) => string | null): string[];
|
|
87
|
+
/** Minimal shape of a conversation detail row inspected when resolving continuity routing. */
|
|
88
|
+
export interface ContinuityHistoryMessage {
|
|
89
|
+
ID?: string | null;
|
|
90
|
+
Role?: string | null;
|
|
91
|
+
AgentID?: string | null;
|
|
92
|
+
ParentID?: string | null;
|
|
93
|
+
Message?: string | null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns true when a user message explicitly `@`-mentions the given agent (by JSON mention token
|
|
97
|
+
* `@{..."id":"<agentId>"...}`, JSON token `@{..."name":"<agentName>"...}`, or plain `@<agentName>`).
|
|
98
|
+
*/
|
|
99
|
+
export declare function MessageMentionsAgent(messageText: string | null | undefined, agentId: string | null | undefined, agentName?: string | null): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Walks conversation history from newest to oldest to find the most recent non-manager `AI` message
|
|
102
|
+
* that owns continuity for the next turn.
|
|
103
|
+
*
|
|
104
|
+
* Stops and returns `null` (returning ownership to the conversation manager) when:
|
|
105
|
+
* - A `User` message explicitly `@`-mentions the conversation manager and no non-manager `AI`
|
|
106
|
+
* message has replied since (e.g. the user said `@Sage …` and Sage answered directly).
|
|
107
|
+
* - An `AI` message from the conversation manager was handed back from another `AI` message
|
|
108
|
+
* (`ParentID` references an `AI` message, or the immediately preceding row is a non-manager `AI`
|
|
109
|
+
* message in the same turn).
|
|
110
|
+
*/
|
|
111
|
+
export declare function FindContinuityAgentMessage<T extends ContinuityHistoryMessage>(messages: readonly T[], conversationManagerAgentId: string | null, options?: {
|
|
112
|
+
conversationManagerName?: string | null;
|
|
113
|
+
isWithinFloor?: (msg: T) => boolean;
|
|
114
|
+
userMentionsManager?: (msg: T) => boolean;
|
|
115
|
+
}): T | null;
|
|
116
|
+
/**
|
|
117
|
+
* Convenience wrapper around {@link FindContinuityAgentMessage} returning only the `AgentID`.
|
|
118
|
+
*/
|
|
119
|
+
export declare function FindContinuityAgentId<T extends ContinuityHistoryMessage>(messages: readonly T[], conversationManagerAgentId: string | null, options?: {
|
|
120
|
+
conversationManagerName?: string | null;
|
|
121
|
+
isWithinFloor?: (msg: T) => boolean;
|
|
122
|
+
userMentionsManager?: (msg: T) => boolean;
|
|
123
|
+
}): string | null;
|
|
69
124
|
export {};
|
|
70
125
|
//# sourceMappingURL=agent-turn-routing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-turn-routing.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/agent-turn-routing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"agent-turn-routing.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/agent-turn-routing.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,27 +6,57 @@
|
|
|
6
6
|
* @module @memberjunction/ng-conversations
|
|
7
7
|
*/
|
|
8
8
|
import { IsAgentAllowed } from '@memberjunction/ai-core-plus';
|
|
9
|
+
import { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';
|
|
10
|
+
/**
|
|
11
|
+
* True when a message with no allowed `@Agent` mention is intentionally addressed to a person
|
|
12
|
+
* rather than an agent: it tags a person (`@Person`), replies (`ParentID`) to another person's
|
|
13
|
+
* message, or was judged human-addressed by the routing decision.
|
|
14
|
+
*/
|
|
15
|
+
export function IsAddressedToHuman(candidates) {
|
|
16
|
+
return (candidates.MentionedUserIds?.length ?? 0) > 0
|
|
17
|
+
|| Boolean(candidates.ReplyParentIsHuman)
|
|
18
|
+
|| Boolean(candidates.DecisionAddressedToHuman);
|
|
19
|
+
}
|
|
9
20
|
/**
|
|
10
21
|
* Picks the agent and route for one message, or null when no turn starts.
|
|
11
22
|
*
|
|
12
|
-
* The order is MJ's: the first tagged agent the host allows,
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
23
|
+
* The order is MJ's: the first tagged agent the host allows, a direct thread reply (`ParentID`) to
|
|
24
|
+
* a runnable agent, a continuity candidate that a routing decision chose (`DecisionRouted`), and
|
|
25
|
+
* then (under `Always` only) the last agent that answered, the conversation's pinned agent, the
|
|
26
|
+
* host's default agent, and the conversation manager. A message that tags a person, replies to
|
|
27
|
+
* another person's message, or receives a `NoAgent` routing decision with no allowed `@Agent` tag
|
|
28
|
+
* starts no turn. A candidate the host doesn't allow is skipped.
|
|
16
29
|
*
|
|
17
30
|
* A continuity or default candidate that `isKnownAgent` doesn't recognise falls back to the
|
|
18
31
|
* conversation manager, as MJ always has, rather than to the next route. A tagged agent is taken
|
|
19
|
-
* as tagged
|
|
32
|
+
* as tagged; a `ThreadReply` target must pass `isRunnableAgent`.
|
|
20
33
|
*
|
|
21
34
|
* @param candidates The agent each route would use.
|
|
22
35
|
* @param rules The host's reply mode and allowed list.
|
|
23
36
|
* @param isKnownAgent True when the agent exists in the client's agent catalog.
|
|
37
|
+
* @param isRunnableAgent True when the current user can run the agent directly (defaults to `isKnownAgent`).
|
|
24
38
|
*/
|
|
25
|
-
export function ResolveAgentTurn(candidates, rules, isKnownAgent) {
|
|
39
|
+
export function ResolveAgentTurn(candidates, rules, isKnownAgent, isRunnableAgent = isKnownAgent) {
|
|
26
40
|
const mentioned = candidates.MentionedAgentIds.find(id => IsAgentAllowed(id, rules.AllowedAgentIDs));
|
|
27
41
|
if (mentioned) {
|
|
28
42
|
return { AgentId: mentioned, Route: 'Mention' };
|
|
29
43
|
}
|
|
44
|
+
if ((candidates.MentionedUserIds?.length ?? 0) > 0 || candidates.ReplyParentIsHuman) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const replyAgent = candidates.ReplyParentAgentId;
|
|
48
|
+
if (replyAgent && IsAgentAllowed(replyAgent, rules.AllowedAgentIDs) && isRunnableAgent(replyAgent)) {
|
|
49
|
+
return { AgentId: replyAgent, Route: 'ThreadReply' };
|
|
50
|
+
}
|
|
51
|
+
if (candidates.DecisionAddressedToHuman) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (candidates.ContinuityDecisionRouted
|
|
55
|
+
&& candidates.ContinuityAgentId
|
|
56
|
+
&& IsAgentAllowed(candidates.ContinuityAgentId, rules.AllowedAgentIDs)
|
|
57
|
+
&& isKnownAgent(candidates.ContinuityAgentId)) {
|
|
58
|
+
return { AgentId: candidates.ContinuityAgentId, Route: 'DecisionRouted' };
|
|
59
|
+
}
|
|
30
60
|
if (rules.ReplyMode === 'MentionOnly') {
|
|
31
61
|
return null;
|
|
32
62
|
}
|
|
@@ -81,4 +111,115 @@ function agentNameOf(configuration) {
|
|
|
81
111
|
}
|
|
82
112
|
return null;
|
|
83
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns true when a user message explicitly `@`-mentions the given agent (by JSON mention token
|
|
116
|
+
* `@{..."id":"<agentId>"...}`, JSON token `@{..."name":"<agentName>"...}`, or plain `@<agentName>`).
|
|
117
|
+
*/
|
|
118
|
+
export function MessageMentionsAgent(messageText, agentId, agentName) {
|
|
119
|
+
if (!messageText) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const normalizedId = NormalizeUUID(agentId ?? '');
|
|
123
|
+
const normalizedName = (agentName ?? '').trim().toLowerCase();
|
|
124
|
+
let cursor = 0;
|
|
125
|
+
while (cursor < messageText.length) {
|
|
126
|
+
const openIdx = messageText.indexOf('@{', cursor);
|
|
127
|
+
if (openIdx === -1) {
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
const closeIdx = messageText.indexOf('}', openIdx + 2);
|
|
131
|
+
if (closeIdx === -1) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
const body = messageText.slice(openIdx + 2, closeIdx);
|
|
135
|
+
cursor = closeIdx + 1;
|
|
136
|
+
if (!body) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(`{${body}}`);
|
|
141
|
+
if (parsed.type && parsed.type !== 'agent') {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (normalizedId && typeof parsed.id === 'string' && UUIDsEqual(parsed.id, normalizedId)) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (normalizedName && typeof parsed.name === 'string' && parsed.name.trim().toLowerCase() === normalizedName) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Ignore malformed token and continue scanning
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (normalizedName) {
|
|
156
|
+
const escaped = normalizedName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
157
|
+
const plainMention = new RegExp(`(?:^|\\s)@${escaped}(?=\\b|\\s|$)`, 'i');
|
|
158
|
+
if (plainMention.test(messageText)) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Walks conversation history from newest to oldest to find the most recent non-manager `AI` message
|
|
166
|
+
* that owns continuity for the next turn.
|
|
167
|
+
*
|
|
168
|
+
* Stops and returns `null` (returning ownership to the conversation manager) when:
|
|
169
|
+
* - A `User` message explicitly `@`-mentions the conversation manager and no non-manager `AI`
|
|
170
|
+
* message has replied since (e.g. the user said `@Sage …` and Sage answered directly).
|
|
171
|
+
* - An `AI` message from the conversation manager was handed back from another `AI` message
|
|
172
|
+
* (`ParentID` references an `AI` message, or the immediately preceding row is a non-manager `AI`
|
|
173
|
+
* message in the same turn).
|
|
174
|
+
*/
|
|
175
|
+
export function FindContinuityAgentMessage(messages, conversationManagerAgentId, options) {
|
|
176
|
+
const normalizedManagerId = NormalizeUUID(conversationManagerAgentId ?? '');
|
|
177
|
+
const managerName = options?.conversationManagerName ?? 'Sage';
|
|
178
|
+
const byId = new Map();
|
|
179
|
+
for (const m of messages) {
|
|
180
|
+
if (m.ID) {
|
|
181
|
+
byId.set(NormalizeUUID(m.ID), m);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
185
|
+
const msg = messages[i];
|
|
186
|
+
if (options?.isWithinFloor && !options.isWithinFloor(msg)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (msg.Role === 'AI' && msg.AgentID) {
|
|
190
|
+
const isManager = normalizedManagerId.length > 0 && UUIDsEqual(msg.AgentID, normalizedManagerId);
|
|
191
|
+
if (!isManager) {
|
|
192
|
+
return msg;
|
|
193
|
+
}
|
|
194
|
+
// Conversation manager AI row: check if it is a handback from another AI row
|
|
195
|
+
const parent = msg.ParentID ? byId.get(NormalizeUUID(msg.ParentID)) : undefined;
|
|
196
|
+
if (parent && parent.Role === 'AI') {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const prev = i > 0 ? messages[i - 1] : undefined;
|
|
200
|
+
if (prev &&
|
|
201
|
+
prev.Role === 'AI' &&
|
|
202
|
+
prev.AgentID &&
|
|
203
|
+
!UUIDsEqual(prev.AgentID, normalizedManagerId)) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (msg.Role === 'User') {
|
|
209
|
+
const mentionsManager = options?.userMentionsManager
|
|
210
|
+
? options.userMentionsManager(msg)
|
|
211
|
+
: MessageMentionsAgent(msg.Message, conversationManagerAgentId, managerName);
|
|
212
|
+
if (mentionsManager) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Convenience wrapper around {@link FindContinuityAgentMessage} returning only the `AgentID`.
|
|
221
|
+
*/
|
|
222
|
+
export function FindContinuityAgentId(messages, conversationManagerAgentId, options) {
|
|
223
|
+
return FindContinuityAgentMessage(messages, conversationManagerAgentId, options)?.AgentID ?? null;
|
|
224
|
+
}
|
|
84
225
|
//# sourceMappingURL=agent-turn-routing.js.map
|