@bpmsoftwaresolutions/ai-engine-client 1.1.85 → 1.1.87
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/package.json +1 -1
- package/src/client.js +1689 -3768
- package/src/domains/collaboration.js +273 -0
- package/src/domains/message-watch.js +270 -0
- package/src/domains/ping-pong.js +358 -0
- package/src/domains/presence.js +457 -0
- package/src/domains/repo.js +124 -21
- package/src/domains/reports.js +16 -1
- package/src/domains/retrieval-management.js +19 -10
- package/src/domains/retrieval-wrapper.js +39 -3
- package/src/domains/transfer-bundles.js +372 -0
- package/src/domains/transfer-channels.js +664 -0
- package/src/domains/workflows.js +99 -0
- package/src/index.js +1 -1
- package/src/utils/communication.js +92 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { AI_ENGINE_CLIENT_VERSION } from '../constants/client.js';
|
|
2
|
+
import { cleanText, isPlainObject } from '../utils/communication.js';
|
|
3
|
+
|
|
4
|
+
function compareSemanticVersions(left, right) {
|
|
5
|
+
const leftParts = String(left || '').split('.').map((part) => Number.parseInt(part, 10) || 0);
|
|
6
|
+
const rightParts = String(right || '').split('.').map((part) => Number.parseInt(part, 10) || 0);
|
|
7
|
+
const width = Math.max(leftParts.length, rightParts.length);
|
|
8
|
+
for (let index = 0; index < width; index += 1) {
|
|
9
|
+
const a = leftParts[index] || 0;
|
|
10
|
+
const b = rightParts[index] || 0;
|
|
11
|
+
if (a > b) return 1;
|
|
12
|
+
if (a < b) return -1;
|
|
13
|
+
}
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function buildEligibilityError(message, details = {}) {
|
|
18
|
+
const error = new Error(message);
|
|
19
|
+
error.details = details;
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createPingPongDomain(client) {
|
|
24
|
+
return {
|
|
25
|
+
checkCoordinationPingPongPreflight: (request) => checkCoordinationPingPongPreflight(client, request),
|
|
26
|
+
startCoordinationPingPong: (request) => startCoordinationPingPong(client, request),
|
|
27
|
+
sendCoordinationPing: (request) => sendCoordinationPing(client, request),
|
|
28
|
+
sendCoordinationPong: (request) => sendCoordinationPong(client, request),
|
|
29
|
+
getCoordinationPingPongStatus: (request) => getCoordinationPingPongStatus(client, request),
|
|
30
|
+
stopCoordinationPingPong: (request) => stopCoordinationPingPong(client, request),
|
|
31
|
+
pingAgentCommunicationPeer: (request) => sendCoordinationPing(client, request),
|
|
32
|
+
pongAgentCommunicationPeer: (request) => sendCoordinationPong(client, request),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function checkCoordinationPingPongPreflight(client, { packageVersion } = {}) {
|
|
37
|
+
const capabilities = await client.getCollaborationCapabilities();
|
|
38
|
+
const requiredMethods = [
|
|
39
|
+
'startCoordinationPingPong',
|
|
40
|
+
'sendCoordinationPing',
|
|
41
|
+
'sendCoordinationPong',
|
|
42
|
+
'getCoordinationPingPongStatus',
|
|
43
|
+
'stopCoordinationPingPong',
|
|
44
|
+
];
|
|
45
|
+
const resolvedPackageVersion = cleanText(packageVersion) || AI_ENGINE_CLIENT_VERSION;
|
|
46
|
+
const minimumClientVersion = cleanText(capabilities.minimum_client_version) || resolvedPackageVersion;
|
|
47
|
+
const supported = Boolean(capabilities.coordination_ping_pong_supported);
|
|
48
|
+
const reportedMethods = Array.isArray(capabilities.required_methods) && capabilities.required_methods.length > 0
|
|
49
|
+
? capabilities.required_methods
|
|
50
|
+
: capabilities.collaboration_choreography_methods;
|
|
51
|
+
const missingMethods = requiredMethods.filter((method) => !Array.isArray(reportedMethods) || !reportedMethods.includes(method));
|
|
52
|
+
const issues = [];
|
|
53
|
+
if (!supported) {
|
|
54
|
+
issues.push({
|
|
55
|
+
drift_type: 'capability_drift',
|
|
56
|
+
rule: 'coordination_ping_pong_unsupported',
|
|
57
|
+
message: 'Coordination Ping Pong is not supported by the deployed collaboration capability surface.',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (compareSemanticVersions(resolvedPackageVersion, minimumClientVersion) < 0) {
|
|
61
|
+
issues.push({
|
|
62
|
+
drift_type: 'capability_drift',
|
|
63
|
+
rule: 'package_version_below_minimum',
|
|
64
|
+
message: `Package version ${resolvedPackageVersion} is below required minimum ${minimumClientVersion}.`,
|
|
65
|
+
package_version: resolvedPackageVersion,
|
|
66
|
+
required_minimum_version: minimumClientVersion,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (missingMethods.length > 0) {
|
|
70
|
+
issues.push({
|
|
71
|
+
drift_type: 'deployment_drift',
|
|
72
|
+
rule: 'missing_ping_pong_methods',
|
|
73
|
+
message: `Missing required coordination methods: ${missingMethods.join(', ')}.`,
|
|
74
|
+
missing_methods: missingMethods,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (issues.length > 0) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
supported: false,
|
|
81
|
+
minimum_client_version: minimumClientVersion,
|
|
82
|
+
package_version: resolvedPackageVersion,
|
|
83
|
+
coordination_ping_pong_supported: supported,
|
|
84
|
+
required_methods: requiredMethods,
|
|
85
|
+
capabilities,
|
|
86
|
+
issues,
|
|
87
|
+
stop_reason: buildEligibilityError('Coordination Ping Pong is not ready to start.', { issues }),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
ok: true,
|
|
92
|
+
supported: true,
|
|
93
|
+
minimum_client_version: minimumClientVersion,
|
|
94
|
+
package_version: resolvedPackageVersion,
|
|
95
|
+
coordination_ping_pong_supported: supported,
|
|
96
|
+
required_methods: requiredMethods,
|
|
97
|
+
capabilities,
|
|
98
|
+
issues: [],
|
|
99
|
+
stop_reason: null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function resolveCoordinationChannelContext(client, {
|
|
104
|
+
transferChannelId,
|
|
105
|
+
transfer_channel_id,
|
|
106
|
+
channelId,
|
|
107
|
+
channel_id,
|
|
108
|
+
workTransferPacketId,
|
|
109
|
+
work_transfer_packet_id,
|
|
110
|
+
packetId,
|
|
111
|
+
packet_id,
|
|
112
|
+
workflowRunId,
|
|
113
|
+
workflow_run_id,
|
|
114
|
+
participantRole,
|
|
115
|
+
participant_role,
|
|
116
|
+
role,
|
|
117
|
+
} = {}) {
|
|
118
|
+
const normalizedTransferChannelId = cleanText(transfer_channel_id) || cleanText(transferChannelId) || cleanText(channel_id) || cleanText(channelId);
|
|
119
|
+
const normalizedParticipantRole = cleanText(participant_role) || cleanText(participantRole) || cleanText(role);
|
|
120
|
+
const normalizedWorkflowRunId = cleanText(workflow_run_id) || cleanText(workflowRunId);
|
|
121
|
+
if (normalizedTransferChannelId) {
|
|
122
|
+
let packetId = cleanText(work_transfer_packet_id) || cleanText(workTransferPacketId) || cleanText(packet_id) || cleanText(packetId);
|
|
123
|
+
if (!packetId) {
|
|
124
|
+
try {
|
|
125
|
+
const status = await client.getCommunicationChannelStatus({ transferChannelId: normalizedTransferChannelId });
|
|
126
|
+
packetId = cleanText(status?.packet_id || status?.packetId || status?.work_transfer_packet_id);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
void error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { channel_id: normalizedTransferChannelId, packet_id: packetId, workflow_run_id: normalizedWorkflowRunId, stop_reason: null };
|
|
132
|
+
}
|
|
133
|
+
const presence = await client.whoIsOnline({ workflowRunId: normalizedWorkflowRunId, participantRole: normalizedParticipantRole, includeStale: true });
|
|
134
|
+
const directoryRows = Array.isArray(presence?.presence)
|
|
135
|
+
? presence.presence.filter((row) => row && typeof row === 'object')
|
|
136
|
+
: Array.isArray(presence?.online_participants)
|
|
137
|
+
? presence.online_participants.filter((row) => row && typeof row === 'object')
|
|
138
|
+
: Array.isArray(presence?.participants)
|
|
139
|
+
? presence.participants.filter((row) => row && typeof row === 'object')
|
|
140
|
+
: [];
|
|
141
|
+
const filteredRows = normalizedParticipantRole
|
|
142
|
+
? directoryRows.filter((row) => cleanText(row.participant_role) === normalizedParticipantRole)
|
|
143
|
+
: [];
|
|
144
|
+
const onlineRows = filteredRows.filter((row) => Boolean(row.is_online));
|
|
145
|
+
const chooseRows = onlineRows.length > 0 ? onlineRows : filteredRows;
|
|
146
|
+
if (chooseRows.length === 1) {
|
|
147
|
+
return {
|
|
148
|
+
channel_id: cleanText(chooseRows[0].transfer_channel_id),
|
|
149
|
+
packet_id: cleanText(chooseRows[0].work_transfer_packet_id) || cleanText(chooseRows[0].packet_id),
|
|
150
|
+
workflow_run_id: cleanText(chooseRows[0].workflow_run_id) || normalizedWorkflowRunId,
|
|
151
|
+
stop_reason: null,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const openChannels = await client.listOpenCommunicationChannels({ workflowRunId: normalizedWorkflowRunId });
|
|
155
|
+
const openChannelRows = Array.isArray(openChannels) ? openChannels.filter((row) => row && typeof row === 'object') : [];
|
|
156
|
+
if (openChannelRows.length === 1) {
|
|
157
|
+
return {
|
|
158
|
+
channel_id: cleanText(openChannelRows[0].channel_id || openChannelRows[0].transfer_channel_id),
|
|
159
|
+
packet_id: cleanText(openChannelRows[0].packet_id || openChannelRows[0].work_transfer_packet_id),
|
|
160
|
+
workflow_run_id: cleanText(openChannelRows[0].workflow_run_id) || normalizedWorkflowRunId,
|
|
161
|
+
stop_reason: null,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (openChannelRows.length > 1) {
|
|
165
|
+
return {
|
|
166
|
+
channel_id: null,
|
|
167
|
+
packet_id: null,
|
|
168
|
+
workflow_run_id: normalizedWorkflowRunId,
|
|
169
|
+
stop_reason: {
|
|
170
|
+
code: 'multiple_open_channels_need_disambiguation',
|
|
171
|
+
message: 'Multiple open channels matched the requested coordination context.',
|
|
172
|
+
details: {
|
|
173
|
+
workflow_run_id: normalizedWorkflowRunId,
|
|
174
|
+
participant_role: normalizedParticipantRole,
|
|
175
|
+
candidate_count: openChannelRows.length,
|
|
176
|
+
candidate_channel_ids: openChannelRows.map((row) => cleanText(row.channel_id || row.transfer_channel_id)).filter(Boolean),
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
channel_id: null,
|
|
183
|
+
packet_id: null,
|
|
184
|
+
workflow_run_id: normalizedWorkflowRunId,
|
|
185
|
+
stop_reason: {
|
|
186
|
+
code: 'active_channel_not_found',
|
|
187
|
+
message: 'Active transfer channel not found.',
|
|
188
|
+
details: { workflow_run_id: normalizedWorkflowRunId, participant_role: normalizedParticipantRole },
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function startCoordinationPingPong(client, request = {}) {
|
|
194
|
+
const resolved = await resolveCoordinationChannelContext(client, request);
|
|
195
|
+
if (resolved.stop_reason) {
|
|
196
|
+
return { started: false, sent: false, stop_reason: resolved.stop_reason };
|
|
197
|
+
}
|
|
198
|
+
const preflight = await checkCoordinationPingPongPreflight(client, { packageVersion: client.clientVersion });
|
|
199
|
+
if (!preflight.ok) {
|
|
200
|
+
return { started: false, sent: false, stop_reason: preflight.stop_reason, coordination_ping_pong_preflight: preflight };
|
|
201
|
+
}
|
|
202
|
+
const {
|
|
203
|
+
participantRole,
|
|
204
|
+
participant_role,
|
|
205
|
+
role,
|
|
206
|
+
expectedPeerRole,
|
|
207
|
+
expected_peer_role,
|
|
208
|
+
expectedMessageKind,
|
|
209
|
+
expected_message_kind,
|
|
210
|
+
workTransferPacketId,
|
|
211
|
+
work_transfer_packet_id,
|
|
212
|
+
packetId,
|
|
213
|
+
packet_id,
|
|
214
|
+
workflowRunId,
|
|
215
|
+
workflow_run_id,
|
|
216
|
+
proposalId,
|
|
217
|
+
proposal_id,
|
|
218
|
+
mode,
|
|
219
|
+
bodyMarkdown,
|
|
220
|
+
body_markdown,
|
|
221
|
+
payload = {},
|
|
222
|
+
scope = {},
|
|
223
|
+
metadata = {},
|
|
224
|
+
staleAfterSeconds,
|
|
225
|
+
stale_after_seconds,
|
|
226
|
+
} = request;
|
|
227
|
+
return client._request(`/api/agent-communications/transfer-channels/${encodeURIComponent(resolved.channel_id)}/coordination/ping-pong/start`, {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
body: {
|
|
230
|
+
transfer_channel_id: resolved.channel_id,
|
|
231
|
+
participant_role: cleanText(participant_role) || cleanText(participantRole) || cleanText(role),
|
|
232
|
+
expected_peer_role: cleanText(expected_peer_role) || cleanText(expectedPeerRole),
|
|
233
|
+
expected_message_kind: cleanText(expected_message_kind) || cleanText(expectedMessageKind),
|
|
234
|
+
work_transfer_packet_id: cleanText(work_transfer_packet_id) || cleanText(workTransferPacketId) || cleanText(packet_id) || cleanText(packetId),
|
|
235
|
+
workflow_run_id: cleanText(workflow_run_id) || cleanText(workflowRunId) || resolved.workflow_run_id,
|
|
236
|
+
proposal_id: cleanText(proposal_id) || cleanText(proposalId),
|
|
237
|
+
mode: cleanText(mode) || 'communication_participant',
|
|
238
|
+
body_markdown: cleanText(body_markdown) || cleanText(bodyMarkdown),
|
|
239
|
+
payload: isPlainObject(payload) ? payload : {},
|
|
240
|
+
scope: isPlainObject(scope) ? scope : {},
|
|
241
|
+
metadata: isPlainObject(metadata) ? metadata : {},
|
|
242
|
+
stale_after_seconds: stale_after_seconds ?? staleAfterSeconds,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function sendCoordinationPing(client, request = {}) {
|
|
248
|
+
const resolved = await resolveCoordinationChannelContext(client, request);
|
|
249
|
+
if (resolved.stop_reason) return { sent: false, stop_reason: resolved.stop_reason };
|
|
250
|
+
const {
|
|
251
|
+
participantRole,
|
|
252
|
+
participant_role,
|
|
253
|
+
role,
|
|
254
|
+
expectedPeerRole,
|
|
255
|
+
expected_peer_role,
|
|
256
|
+
bodyMarkdown,
|
|
257
|
+
body_markdown,
|
|
258
|
+
payload = {},
|
|
259
|
+
scope = {},
|
|
260
|
+
metadata = {},
|
|
261
|
+
workflowRunId,
|
|
262
|
+
workflow_run_id,
|
|
263
|
+
workTransferPacketId,
|
|
264
|
+
work_transfer_packet_id,
|
|
265
|
+
packetId,
|
|
266
|
+
packet_id,
|
|
267
|
+
proposalId,
|
|
268
|
+
proposal_id,
|
|
269
|
+
expectedMessageKind,
|
|
270
|
+
expected_message_kind,
|
|
271
|
+
} = request;
|
|
272
|
+
return client._request(`/api/agent-communications/transfer-channels/${encodeURIComponent(resolved.channel_id)}/coordination/ping-pong/ping`, {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
body: {
|
|
275
|
+
transfer_channel_id: resolved.channel_id,
|
|
276
|
+
participant_role: cleanText(participant_role) || cleanText(participantRole) || cleanText(role),
|
|
277
|
+
expected_peer_role: cleanText(expected_peer_role) || cleanText(expectedPeerRole),
|
|
278
|
+
body_markdown: cleanText(body_markdown) || cleanText(bodyMarkdown),
|
|
279
|
+
payload: isPlainObject(payload) ? payload : {},
|
|
280
|
+
scope: isPlainObject(scope) ? scope : {},
|
|
281
|
+
metadata: isPlainObject(metadata) ? metadata : {},
|
|
282
|
+
workflow_run_id: cleanText(workflow_run_id) || cleanText(workflowRunId) || resolved.workflow_run_id,
|
|
283
|
+
work_transfer_packet_id: cleanText(work_transfer_packet_id) || cleanText(workTransferPacketId) || cleanText(packet_id) || cleanText(packetId),
|
|
284
|
+
proposal_id: cleanText(proposal_id) || cleanText(proposalId),
|
|
285
|
+
expected_message_kind: cleanText(expected_message_kind) || cleanText(expectedMessageKind),
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export async function sendCoordinationPong(client, request = {}) {
|
|
291
|
+
const resolved = await resolveCoordinationChannelContext(client, request);
|
|
292
|
+
if (resolved.stop_reason) return { sent: false, stop_reason: resolved.stop_reason };
|
|
293
|
+
const {
|
|
294
|
+
participantRole,
|
|
295
|
+
participant_role,
|
|
296
|
+
role,
|
|
297
|
+
expectedPeerRole,
|
|
298
|
+
expected_peer_role,
|
|
299
|
+
bodyMarkdown,
|
|
300
|
+
body_markdown,
|
|
301
|
+
payload = {},
|
|
302
|
+
scope = {},
|
|
303
|
+
metadata = {},
|
|
304
|
+
workflowRunId,
|
|
305
|
+
workflow_run_id,
|
|
306
|
+
workTransferPacketId,
|
|
307
|
+
work_transfer_packet_id,
|
|
308
|
+
packetId,
|
|
309
|
+
packet_id,
|
|
310
|
+
proposalId,
|
|
311
|
+
proposal_id,
|
|
312
|
+
expectedMessageKind,
|
|
313
|
+
expected_message_kind,
|
|
314
|
+
} = request;
|
|
315
|
+
return client._request(`/api/agent-communications/transfer-channels/${encodeURIComponent(resolved.channel_id)}/coordination/ping-pong/pong`, {
|
|
316
|
+
method: 'POST',
|
|
317
|
+
body: {
|
|
318
|
+
transfer_channel_id: resolved.channel_id,
|
|
319
|
+
participant_role: cleanText(participant_role) || cleanText(participantRole) || cleanText(role),
|
|
320
|
+
expected_peer_role: cleanText(expected_peer_role) || cleanText(expectedPeerRole),
|
|
321
|
+
body_markdown: cleanText(body_markdown) || cleanText(bodyMarkdown),
|
|
322
|
+
payload: isPlainObject(payload) ? payload : {},
|
|
323
|
+
scope: isPlainObject(scope) ? scope : {},
|
|
324
|
+
metadata: isPlainObject(metadata) ? metadata : {},
|
|
325
|
+
workflow_run_id: cleanText(workflow_run_id) || cleanText(workflowRunId) || resolved.workflow_run_id,
|
|
326
|
+
work_transfer_packet_id: cleanText(work_transfer_packet_id) || cleanText(workTransferPacketId) || cleanText(packet_id) || cleanText(packetId),
|
|
327
|
+
proposal_id: cleanText(proposal_id) || cleanText(proposalId),
|
|
328
|
+
expected_message_kind: cleanText(expected_message_kind) || cleanText(expectedMessageKind),
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export async function getCoordinationPingPongStatus(client, request = {}) {
|
|
334
|
+
const resolved = await resolveCoordinationChannelContext(client, request);
|
|
335
|
+
if (resolved.stop_reason) return { verified: false, stop_reason: resolved.stop_reason };
|
|
336
|
+
const { participantRole, participant_role, role, workflowRunId, workflow_run_id } = request;
|
|
337
|
+
return client._request(`/api/agent-communications/transfer-channels/${encodeURIComponent(resolved.channel_id)}/coordination/ping-pong/status`, {
|
|
338
|
+
query: {
|
|
339
|
+
participant_role: cleanText(participant_role) || cleanText(participantRole) || cleanText(role),
|
|
340
|
+
workflow_run_id: cleanText(workflow_run_id) || cleanText(workflowRunId) || resolved.workflow_run_id,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export async function stopCoordinationPingPong(client, request = {}) {
|
|
346
|
+
const resolved = await resolveCoordinationChannelContext(client, request);
|
|
347
|
+
if (resolved.stop_reason) return { stopped: false, stop_reason: resolved.stop_reason };
|
|
348
|
+
const { participantRole, participant_role, role, workflowRunId, workflow_run_id, metadata = {} } = request;
|
|
349
|
+
return client._request(`/api/agent-communications/transfer-channels/${encodeURIComponent(resolved.channel_id)}/coordination/ping-pong/stop`, {
|
|
350
|
+
method: 'POST',
|
|
351
|
+
body: {
|
|
352
|
+
transfer_channel_id: resolved.channel_id,
|
|
353
|
+
participant_role: cleanText(participant_role) || cleanText(participantRole) || cleanText(role),
|
|
354
|
+
workflow_run_id: cleanText(workflow_run_id) || cleanText(workflowRunId) || resolved.workflow_run_id,
|
|
355
|
+
metadata: isPlainObject(metadata) ? metadata : {},
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
}
|