@bpmsoftwaresolutions/ai-engine-client 1.1.95 → 1.1.97

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.
@@ -0,0 +1,885 @@
1
+ import { cleanText, isPlainObject, normalizeConnectionFirstMessageKind, normalizeTransferKind } from '../utils/communication.js';
2
+
3
+ function normalizeTransferChannelId(request = {}) {
4
+ return cleanText(request.transfer_channel_id) || cleanText(request.transferChannelId) || cleanText(request.channel_id) || cleanText(request.channelId);
5
+ }
6
+
7
+ export function createAgentCommunicationsDomain(client) {
8
+ return {
9
+ startAgentConnection: (request) => startAgentConnection(client, request),
10
+ establishAgentCommunicationChannel: (request) => establishAgentCommunicationChannel(client, request),
11
+ runInterAgentMessagingLoop: (request) => runInterAgentMessagingLoop(client, request),
12
+ };
13
+ }
14
+
15
+ export async function startAgentConnection(client, {
16
+ workflowRunId,
17
+ workflow_run_id,
18
+ upstreamAgent,
19
+ upstream_agent,
20
+ upstreamAgentSessionId,
21
+ upstream_agent_session_id,
22
+ upstreamAgentId,
23
+ upstream_agent_id,
24
+ upstreamRole,
25
+ upstream_role,
26
+ recipientMode,
27
+ recipient_mode,
28
+ purpose,
29
+ objective,
30
+ mode = 'handoff',
31
+ firstMessage = {},
32
+ first_message = {},
33
+ expectedMessageKind,
34
+ expected_message_kind,
35
+ participantRole,
36
+ participant_role,
37
+ participantLabel,
38
+ participant_label,
39
+ senderAgentSessionId,
40
+ sender_agent_session_id,
41
+ senderActorSessionId,
42
+ sender_actor_session_id,
43
+ cleanupPolicy,
44
+ cleanup_policy,
45
+ staleAfterSeconds = 300,
46
+ stale_after_seconds,
47
+ operatorNudge,
48
+ operator_nudge,
49
+ metadata = {},
50
+ } = {}) {
51
+ const normalizedFirstMessage = isPlainObject(firstMessage) ? firstMessage : isPlainObject(first_message) ? first_message : {};
52
+ const normalizedTransferKind = normalizeTransferKind(cleanText(mode) || 'handoff');
53
+ let normalizedWorkflowRunId = cleanText(workflow_run_id) || cleanText(workflowRunId);
54
+ if (!normalizedWorkflowRunId) {
55
+ try {
56
+ const status = await client.currentWorkflowStatus();
57
+ normalizedWorkflowRunId =
58
+ cleanText(status?.workflow_run_id)
59
+ || cleanText(status?.workflowRunId)
60
+ || cleanText(status?.current_workflow_run_id)
61
+ || cleanText(status?.currentWorkflowRunId)
62
+ || cleanText(status?.summary?.workflow_run_id)
63
+ || cleanText(status?.summary?.workflowRunId)
64
+ || cleanText(status?.summary?.current_workflow_run_id)
65
+ || cleanText(status?.summary?.currentWorkflowRunId);
66
+ } catch (error) {
67
+ void error;
68
+ }
69
+ }
70
+ if (!normalizedWorkflowRunId) {
71
+ throw new Error('workflow_run_id is required to start an agent connection.');
72
+ }
73
+ const normalizedUpstreamAgent = cleanText(upstream_agent) || cleanText(upstreamAgent) || cleanText(upstream_agent_session_id) || cleanText(upstreamAgentSessionId) || cleanText(upstream_agent_id) || cleanText(upstreamAgentId);
74
+ const normalizedRecipientMode = cleanText(recipient_mode) || cleanText(recipientMode) || (normalizedUpstreamAgent ? 'agent_session' : 'role');
75
+ const normalizedParticipantRole = cleanText(participant_role) || cleanText(participantRole) || 'downstream';
76
+ const normalizedParticipantLabel = cleanText(participant_label) || cleanText(participantLabel) || normalizedParticipantRole;
77
+ const normalizedSenderAgentSessionId = cleanText(sender_agent_session_id) || cleanText(senderAgentSessionId) || client.agentSessionId;
78
+ const normalizedSenderActorSessionId = cleanText(sender_actor_session_id) || cleanText(senderActorSessionId);
79
+ const normalizedFirstMessageKind = normalizeConnectionFirstMessageKind(
80
+ cleanText(normalizedFirstMessage.kind) || cleanText(normalizedFirstMessage.message_kind) || cleanText(normalizedFirstMessage.messageKind),
81
+ normalizedTransferKind,
82
+ );
83
+ const normalizedExpectedMessageKind = cleanText(expected_message_kind) || cleanText(expectedMessageKind) || 'connection_accepted';
84
+ const normalizedObjective = cleanText(objective) || cleanText(purpose) || cleanText(normalizedFirstMessage.body) || cleanText(normalizedFirstMessage.body_markdown) || cleanText(normalizedFirstMessage.bodyMarkdown) || 'Establish governed coordination.';
85
+ const transferResult = await client.transferWorkPacket({
86
+ workflowRunId: normalizedWorkflowRunId,
87
+ transferKind: normalizedTransferKind,
88
+ objective: normalizedObjective,
89
+ requestedOutcome: cleanText(purpose) || normalizedObjective,
90
+ target: {
91
+ intent: normalizedTransferKind,
92
+ recipient_mode: normalizedRecipientMode,
93
+ preferred_agent_session_id: normalizedUpstreamAgent,
94
+ preferred_role_key: cleanText(upstream_role) || cleanText(upstreamRole),
95
+ },
96
+ artifacts: [],
97
+ issues: [],
98
+ expectedEvidence: [],
99
+ preferredModes: ['inline_payload', 'bundle', 'artifact_refs'],
100
+ capabilities: {},
101
+ senderAgentSessionId: normalizedSenderAgentSessionId,
102
+ senderActorSessionId: normalizedSenderActorSessionId,
103
+ subject: cleanText(purpose) || normalizedObjective,
104
+ bodyMarkdown:
105
+ cleanText(normalizedFirstMessage.body_markdown)
106
+ || cleanText(normalizedFirstMessage.bodyMarkdown)
107
+ || cleanText(normalizedFirstMessage.body)
108
+ || `Connection request: ${normalizedObjective}`,
109
+ cleanupPolicy: cleanText(cleanup_policy) || cleanText(cleanupPolicy),
110
+ messageKind: normalizedFirstMessageKind,
111
+ message_kind: normalizedFirstMessageKind,
112
+ metadata: isPlainObject(metadata) ? metadata : {},
113
+ });
114
+ const transferChannel = transferResult.communication_transfer_channel || {};
115
+ const connection = await client.connectToTransferChannel({
116
+ transferChannelId: transferChannel.transfer_channel_id || transferChannel.channel_id || transferResult.channel_id,
117
+ workTransferPacketId: transferResult.work_transfer_packet?.work_transfer_packet_id || transferResult.packet_id || transferResult.transfer_packet_id,
118
+ workflowRunId: normalizedWorkflowRunId,
119
+ participantRole: normalizedParticipantRole,
120
+ expectedPeerRole: cleanText(upstream_role) || cleanText(upstreamRole) || 'upstream',
121
+ expectedMessageKind: normalizedExpectedMessageKind,
122
+ proposalId: transferResult.transfer_negotiation?.proposal_id,
123
+ mode: 'communication_participant',
124
+ participantLabel: normalizedParticipantLabel,
125
+ senderAgentSessionId: normalizedSenderAgentSessionId,
126
+ senderActorSessionId: normalizedSenderActorSessionId,
127
+ currentPhase: 'channel_ready',
128
+ currentTaskSummary: cleanText(purpose) || normalizedObjective,
129
+ expectedPayload: {
130
+ purpose: cleanText(purpose) || normalizedObjective,
131
+ first_message_kind: normalizedFirstMessageKind,
132
+ first_message_body:
133
+ cleanText(normalizedFirstMessage.body_markdown)
134
+ || cleanText(normalizedFirstMessage.bodyMarkdown)
135
+ || cleanText(normalizedFirstMessage.body)
136
+ || null,
137
+ },
138
+ lastSeenMessageId: transferResult.communication_message?.agent_message_id,
139
+ staleAfterSeconds: stale_after_seconds ?? staleAfterSeconds,
140
+ operatorNudge:
141
+ cleanText(operator_nudge)
142
+ || cleanText(operatorNudge)
143
+ || `upstream, please send ${normalizedExpectedMessageKind}.`,
144
+ observedAt: transferResult.communication_message?.created_at || transferResult.transfer_receipt?.created_at,
145
+ metadata: {
146
+ ...(isPlainObject(metadata) ? metadata : {}),
147
+ start_agent_connection: true,
148
+ },
149
+ });
150
+ const handshake = connection.handshake || {};
151
+ const status = connection.connected ? 'waiting' : (connection.stop_reason ? 'failed' : 'waiting');
152
+ const transferPacketId = transferResult.work_transfer_packet?.work_transfer_packet_id || transferResult.transfer_receipt?.transfer_packet_id || transferResult.packet_id || null;
153
+ const channelId = connection.channel_id || transferChannel.transfer_channel_id || transferChannel.channel_id || null;
154
+ return {
155
+ status,
156
+ channel_id: channelId,
157
+ transfer_packet_id: transferPacketId,
158
+ correlation_id: connection.correlation_id || handshake.correlation_id || null,
159
+ handshake_state: connection.handshake_state || handshake.handshake_state || null,
160
+ waiting_side: connection.waiting_on || connection.expected_from || handshake.waiting_side || null,
161
+ expected_message: connection.expected_message_kind || handshake.expected_message_kind || normalizedExpectedMessageKind,
162
+ last_heartbeat_at: handshake.last_heartbeat_at || connection.collaboration_heartbeat?.observed_at || null,
163
+ last_receipt_id: handshake.last_receipt_id || transferResult.transfer_receipt?.receipt_id || null,
164
+ operator_nudge: connection.operator_nudge || handshake.operator_nudge || null,
165
+ handshake,
166
+ connection,
167
+ transfer: transferResult,
168
+ first_message: {
169
+ kind: normalizedFirstMessageKind,
170
+ body: cleanText(normalizedFirstMessage.body_markdown) || cleanText(normalizedFirstMessage.bodyMarkdown) || cleanText(normalizedFirstMessage.body) || null,
171
+ },
172
+ transfer_channel: transferChannel,
173
+ transfer_receipt: transferResult.transfer_receipt || null,
174
+ communication_message: transferResult.communication_message || null,
175
+ };
176
+ }
177
+
178
+ export async function establishAgentCommunicationChannel(client, {
179
+ projectIdentifier,
180
+ projectId,
181
+ claimId,
182
+ actorId,
183
+ actorMode = 'operator',
184
+ executionIntent = 'establish agent communication channel',
185
+ workflowRunLimit = 5,
186
+ startWorkBody = {},
187
+ startWorkRequest = {},
188
+ resumeProjectWorkOptions = {},
189
+ transferKind = 'coordination',
190
+ objective,
191
+ requestedOutcome,
192
+ transferChannelId,
193
+ transfer_channel_id,
194
+ channelId,
195
+ channel_id,
196
+ workTransferPacketId,
197
+ work_transfer_packet_id,
198
+ packetId,
199
+ packet_id,
200
+ participantRole,
201
+ participant_role,
202
+ participantLabel,
203
+ participant_label,
204
+ participantKind,
205
+ participant_kind,
206
+ agentSessionId,
207
+ agent_session_id,
208
+ actorSessionId,
209
+ actor_session_id,
210
+ upstreamAgentSessionId,
211
+ upstream_agent_session_id,
212
+ upstreamActorSessionId,
213
+ upstream_actor_session_id,
214
+ downstreamAgentSessionId,
215
+ downstream_agent_session_id,
216
+ downstreamActorSessionId,
217
+ downstream_actor_session_id,
218
+ expectedMessageKind = 'connection_accepted',
219
+ expected_message_kind,
220
+ expectedNextMessage,
221
+ expected_next_message,
222
+ currentPhase = 'channel_ready',
223
+ current_phase,
224
+ currentTaskSummary,
225
+ current_task_summary,
226
+ channelKind = 'bidirectional',
227
+ channel_kind,
228
+ includeTransferChannelProjection = true,
229
+ includeChannelPresenceProjection = true,
230
+ includeOperatorProjectionMetadata = true,
231
+ metadata = {},
232
+ } = {}) {
233
+ const missingSurfaces = [];
234
+ const explicitProjectReference = cleanText(projectIdentifier) || cleanText(projectId);
235
+ const actorIdentity = cleanText(actorId) || client.actorId;
236
+ const normalizedExecutionIntent = cleanText(executionIntent) || 'establish agent communication channel';
237
+ const normalizedParticipantRole = cleanText(participant_role) || cleanText(participantRole) || 'downstream';
238
+ const normalizedParticipantLabel = cleanText(participant_label) || cleanText(participantLabel) || normalizedParticipantRole;
239
+ const normalizedParticipantKind = cleanText(participant_kind) || cleanText(participantKind) || (
240
+ cleanText(agent_session_id)
241
+ || cleanText(agentSessionId)
242
+ || cleanText(downstream_agent_session_id)
243
+ || cleanText(downstreamAgentSessionId)
244
+ || cleanText(upstream_agent_session_id)
245
+ || cleanText(upstreamAgentSessionId)
246
+ ) ? 'agent_session' : 'role';
247
+ const normalizedExpectedMessageKind = cleanText(expected_message_kind) || cleanText(expectedMessageKind) || cleanText(expected_next_message) || cleanText(expectedNextMessage) || 'connection_accepted';
248
+ const normalizedCurrentPhase = cleanText(current_phase) || cleanText(currentPhase) || 'channel_ready';
249
+ const normalizedCurrentTaskSummary = cleanText(current_task_summary) || cleanText(currentTaskSummary) || normalizedExecutionIntent;
250
+ const normalizedObjective = cleanText(objective) || cleanText(requestedOutcome) || normalizedExecutionIntent;
251
+ const normalizedChannelKind = cleanText(channel_kind) || cleanText(channelKind) || 'bidirectional';
252
+ const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
253
+ const hasExplicitChannelReference = Boolean(
254
+ cleanText(transfer_channel_id)
255
+ || cleanText(transferChannelId)
256
+ || cleanText(channel_id)
257
+ || cleanText(channelId)
258
+ );
259
+
260
+ let continuation = null;
261
+ let startWorkResult = null;
262
+ if (explicitProjectReference) {
263
+ if (typeof client.resumeProjectWork !== 'function') {
264
+ missingSurfaces.push('resumeProjectWork');
265
+ } else {
266
+ continuation = await client.resumeProjectWork({
267
+ projectIdentifier: explicitProjectReference,
268
+ projectId: explicitProjectReference,
269
+ actorMode,
270
+ executionIntent: normalizedExecutionIntent,
271
+ requireClaim: false,
272
+ workflowRunLimit,
273
+ ...normalizedMetadata.resume_project_work_options,
274
+ ...resumeProjectWorkOptions,
275
+ });
276
+ }
277
+ } else if (Object.keys(isPlainObject(startWorkRequest) ? startWorkRequest : {}).length > 0 || Object.keys(isPlainObject(startWorkBody) ? startWorkBody : {}).length > 0) {
278
+ if (typeof client.startWork !== 'function') {
279
+ missingSurfaces.push('startWork');
280
+ } else {
281
+ startWorkResult = await client.startWork({
282
+ ...(isPlainObject(startWorkBody) ? startWorkBody : {}),
283
+ ...(isPlainObject(startWorkRequest) ? startWorkRequest : {}),
284
+ });
285
+ }
286
+ } else {
287
+ throw new Error('projectIdentifier is required.');
288
+ }
289
+
290
+ const projectPayload = isPlainObject(continuation?.project)
291
+ ? continuation.project
292
+ : isPlainObject(startWorkResult?.project)
293
+ ? startWorkResult.project
294
+ : isPlainObject(continuation?.summary)
295
+ ? continuation.summary
296
+ : {};
297
+ const resolvedProjectId = cleanText(projectPayload.project_id) || cleanText(projectPayload.projectId) || explicitProjectReference;
298
+ const resolvedWorkflowId = cleanText(projectPayload.workflow_id) || cleanText(projectPayload.workflowId) || cleanText(continuation?.workflow_id) || cleanText(startWorkResult?.workflow_id);
299
+ const resolvedWorkflowRunId = cleanText(projectPayload.workflow_run_id) || cleanText(projectPayload.workflowRunId) || cleanText(continuation?.workflow_run_id) || cleanText(startWorkResult?.workflow_run_id) || cleanText(startWorkResult?.workflowRunId);
300
+ if (!resolvedProjectId || !resolvedWorkflowRunId) {
301
+ throw new Error('project/work identifiers are ambiguous.');
302
+ }
303
+
304
+ const transferPacket = await client.transferWorkPacket({
305
+ workflowRunId: resolvedWorkflowRunId,
306
+ transferKind,
307
+ objective: normalizedObjective,
308
+ requestedOutcome: normalizedObjective,
309
+ target: {
310
+ intent: transferKind,
311
+ recipient_mode: normalizedParticipantKind === 'agent_session' ? 'agent_session' : 'role',
312
+ preferred_agent_session_id: cleanText(downstream_agent_session_id) || cleanText(downstreamAgentSessionId) || cleanText(upstream_agent_session_id) || cleanText(upstreamAgentSessionId) || cleanText(agent_session_id) || cleanText(agentSessionId),
313
+ preferred_role_key: normalizedParticipantRole,
314
+ },
315
+ artifacts: [],
316
+ issues: [],
317
+ expectedEvidence: [],
318
+ preferredModes: ['inline_payload', 'bundle', 'artifact_refs'],
319
+ capabilities: {},
320
+ senderAgentSessionId: cleanText(agent_session_id) || cleanText(agentSessionId) || client.agentSessionId,
321
+ senderActorSessionId: cleanText(actor_session_id) || cleanText(actorSessionId),
322
+ subject: normalizedObjective,
323
+ bodyMarkdown: normalizedCurrentTaskSummary,
324
+ messageKind: normalizedExpectedMessageKind,
325
+ metadata: {
326
+ ...normalizedMetadata,
327
+ source: 'establishAgentCommunicationChannel',
328
+ project_id: resolvedProjectId,
329
+ workflow_id: resolvedWorkflowId,
330
+ workflow_run_id: resolvedWorkflowRunId,
331
+ },
332
+ });
333
+ const normalizedTransferPacket = isPlainObject(transferPacket) ? transferPacket : {};
334
+ const transferChannel = isPlainObject(normalizedTransferPacket.communication_transfer_channel)
335
+ ? normalizedTransferPacket.communication_transfer_channel
336
+ : {};
337
+ const resolvedTransferPacketId = cleanText(work_transfer_packet_id)
338
+ || cleanText(workTransferPacketId)
339
+ || cleanText(packet_id)
340
+ || cleanText(packetId)
341
+ || cleanText(normalizedTransferPacket.work_transfer_packet?.work_transfer_packet_id)
342
+ || cleanText(normalizedTransferPacket.transfer_packet_id)
343
+ || cleanText(normalizedTransferPacket.packet_id);
344
+ const resolvedTransferChannelId = cleanText(transfer_channel_id)
345
+ || cleanText(transferChannelId)
346
+ || cleanText(channel_id)
347
+ || cleanText(channelId)
348
+ || cleanText(transferChannel.transfer_channel_id)
349
+ || cleanText(transferChannel.channel_id);
350
+
351
+ if (!resolvedTransferPacketId) {
352
+ throw new Error('transfer packet could not be established.');
353
+ }
354
+ if (!resolvedTransferChannelId) {
355
+ throw new Error('channel ownership is ambiguous.');
356
+ }
357
+
358
+ const openChannelSurface = typeof client.openTransferChannel === 'function'
359
+ ? client.openTransferChannel.bind(client)
360
+ : typeof client.openAgentChannel === 'function'
361
+ ? client.openAgentChannel.bind(client)
362
+ : typeof client.connectToTransferChannel === 'function'
363
+ ? client.connectToTransferChannel.bind(client)
364
+ : null;
365
+ if (!openChannelSurface) missingSurfaces.push('openTransferChannel');
366
+ const openChannel = openChannelSurface
367
+ ? await openChannelSurface({
368
+ transferChannelId: resolvedTransferChannelId,
369
+ workTransferPacketId: resolvedTransferPacketId,
370
+ workflowRunId: resolvedWorkflowRunId,
371
+ channelKind: normalizedChannelKind,
372
+ upstreamAgentSessionId: cleanText(upstream_agent_session_id) || cleanText(upstreamAgentSessionId),
373
+ upstreamActorSessionId: cleanText(upstream_actor_session_id) || cleanText(upstreamActorSessionId),
374
+ downstreamAgentSessionId: cleanText(downstream_agent_session_id) || cleanText(downstreamAgentSessionId) || cleanText(agent_session_id) || cleanText(agentSessionId) || client.agentSessionId,
375
+ downstreamActorSessionId: cleanText(downstream_actor_session_id) || cleanText(downstreamActorSessionId) || cleanText(actor_session_id) || cleanText(actorSessionId),
376
+ evidenceRequiredForClosure: true,
377
+ metadata: {
378
+ ...normalizedMetadata,
379
+ source: 'establishAgentCommunicationChannel',
380
+ project_id: resolvedProjectId,
381
+ workflow_id: resolvedWorkflowId,
382
+ workflow_run_id: resolvedWorkflowRunId,
383
+ },
384
+ })
385
+ : null;
386
+ const openChannelPayload = isPlainObject(openChannel?.transfer_channel)
387
+ ? openChannel.transfer_channel
388
+ : isPlainObject(openChannel?.collaboration_participant)
389
+ ? openChannel.collaboration_participant
390
+ : isPlainObject(openChannel)
391
+ ? openChannel
392
+ : {};
393
+ const openedChannelId = cleanText(openChannelPayload.transfer_channel_id) || cleanText(openChannelPayload.channel_id) || resolvedTransferChannelId;
394
+
395
+ const joinSurfaceName = hasExplicitChannelReference ? 'resumeTransferChannel' : 'joinTransferChannel';
396
+ const joinSurface = typeof client[joinSurfaceName] === 'function'
397
+ ? client[joinSurfaceName].bind(client)
398
+ : typeof client.joinTransferChannel === 'function'
399
+ ? client.joinTransferChannel.bind(client)
400
+ : typeof client.resumeTransferChannel === 'function'
401
+ ? client.resumeTransferChannel.bind(client)
402
+ : null;
403
+ if (!joinSurface) missingSurfaces.push(joinSurfaceName);
404
+ const participantRecord = joinSurface
405
+ ? await joinSurface({
406
+ transferChannelId: openedChannelId,
407
+ workTransferPacketId: resolvedTransferPacketId,
408
+ workflowRunId: resolvedWorkflowRunId,
409
+ participantRole: normalizedParticipantRole,
410
+ participantKind: normalizedParticipantKind,
411
+ participantLabel: normalizedParticipantLabel,
412
+ agentSessionId: cleanText(agent_session_id) || cleanText(agentSessionId) || client.agentSessionId,
413
+ actorSessionId: cleanText(actor_session_id) || cleanText(actorSessionId),
414
+ currentPhase: normalizedCurrentPhase,
415
+ currentTaskSummary: normalizedCurrentTaskSummary,
416
+ lastSeenMessageId: cleanText(normalizedTransferPacket.communication_message?.agent_message_id),
417
+ lastSeenAt: cleanText(normalizedTransferPacket.communication_message?.created_at) || cleanText(openChannel?.created_at),
418
+ lastSeenHeartbeatAt: cleanText(openChannel?.heartbeat?.observed_at) || cleanText(openChannel?.collaboration_heartbeat?.observed_at),
419
+ metadata: {
420
+ ...normalizedMetadata,
421
+ source: 'establishAgentCommunicationChannel',
422
+ project_id: resolvedProjectId,
423
+ workflow_id: resolvedWorkflowId,
424
+ workflow_run_id: resolvedWorkflowRunId,
425
+ },
426
+ })
427
+ : null;
428
+
429
+ const heartbeatSurface = typeof client.postCollaborationHeartbeat === 'function'
430
+ ? client.postCollaborationHeartbeat.bind(client)
431
+ : typeof client.postAgentHeartbeat === 'function'
432
+ ? client.postAgentHeartbeat.bind(client)
433
+ : null;
434
+ if (!heartbeatSurface) missingSurfaces.push('postCollaborationHeartbeat');
435
+ const heartbeat = heartbeatSurface
436
+ ? await heartbeatSurface({
437
+ transferChannelId: openedChannelId,
438
+ workTransferPacketId: resolvedTransferPacketId,
439
+ workflowRunId: resolvedWorkflowRunId,
440
+ participantRole: normalizedParticipantRole,
441
+ activityState: 'active',
442
+ agentSessionId: cleanText(agent_session_id) || cleanText(agentSessionId) || client.agentSessionId,
443
+ actorSessionId: cleanText(actor_session_id) || cleanText(actorSessionId),
444
+ currentPhase: normalizedCurrentPhase,
445
+ currentTaskSummary: normalizedCurrentTaskSummary,
446
+ isActive: true,
447
+ observedAt: cleanText(normalizedTransferPacket.communication_message?.created_at) || cleanText(openChannel?.created_at),
448
+ metadata: {
449
+ ...normalizedMetadata,
450
+ source: 'establishAgentCommunicationChannel',
451
+ project_id: resolvedProjectId,
452
+ workflow_id: resolvedWorkflowId,
453
+ workflow_run_id: resolvedWorkflowRunId,
454
+ },
455
+ })
456
+ : null;
457
+
458
+ const status = await client.getCommunicationChannelStatus({ transferChannelId: openedChannelId }).catch(() => null);
459
+ const participants = await client.getCommunicationChannelParticipants({ transferChannelId: openedChannelId }).catch(() => null);
460
+ const transferChannelProjectionSurface = typeof client.getTransferChannelProjection === 'function'
461
+ ? client.getTransferChannelProjection.bind(client)
462
+ : typeof client.getLogaTransferChannelThreadProjection === 'function'
463
+ ? client.getLogaTransferChannelThreadProjection.bind(client)
464
+ : null;
465
+ if (!transferChannelProjectionSurface) missingSurfaces.push('getTransferChannelProjection');
466
+ const transferChannelProjection = includeTransferChannelProjection && transferChannelProjectionSurface
467
+ ? await transferChannelProjectionSurface(openedChannelId).catch(() => null)
468
+ : null;
469
+ const channelPresence = includeChannelPresenceProjection
470
+ ? await client.getChannelPresence({ transferChannelId: openedChannelId }).catch(() => null)
471
+ : null;
472
+
473
+ const statusPayload = isPlainObject(status) ? status : {};
474
+ const participantPayload = isPlainObject(participants) ? participants : {};
475
+ const participantRows = Array.isArray(participantPayload.participants)
476
+ ? participantPayload.participants.filter((row) => isPlainObject(row))
477
+ : Array.isArray(participantPayload.collaboration_participants)
478
+ ? participantPayload.collaboration_participants.filter((row) => isPlainObject(row))
479
+ : [];
480
+ const waitingSide = cleanText(statusPayload.waiting_side)
481
+ || cleanText(statusPayload.operator_state?.waiting_side)
482
+ || cleanText(openChannel?.waiting_side)
483
+ || cleanText(heartbeat?.waiting_side)
484
+ || null;
485
+ const nextMessage = cleanText(statusPayload.next_message_kind)
486
+ || cleanText(statusPayload.expected_message_kind)
487
+ || cleanText(statusPayload.next_action)
488
+ || cleanText(openChannel?.expected_message_kind)
489
+ || cleanText(heartbeat?.expected_message_kind)
490
+ || normalizedExpectedMessageKind;
491
+ const heartbeatStatus = cleanText(heartbeat?.collaboration_heartbeat?.activity_state)
492
+ || cleanText(heartbeat?.activity_state)
493
+ || cleanText(heartbeat?.status)
494
+ || 'active';
495
+
496
+ const operatorProjectionMetadata = includeOperatorProjectionMetadata
497
+ ? {
498
+ transfer_channel_projection: transferChannelProjection,
499
+ channel_status: statusPayload,
500
+ channel_participants: participantPayload,
501
+ channel_presence: channelPresence,
502
+ }
503
+ : null;
504
+
505
+ const established = Boolean(openedChannelId && resolvedTransferPacketId && heartbeat);
506
+ return {
507
+ status: missingSurfaces.length > 0 ? 'partial' : established ? 'ready' : 'blocked',
508
+ project_id: resolvedProjectId,
509
+ workflow_id: resolvedWorkflowId,
510
+ workflow_run_id: resolvedWorkflowRunId,
511
+ transfer_packet_id: resolvedTransferPacketId,
512
+ channel_id: openedChannelId,
513
+ participants: participantRows.length > 0 ? participantRows : [participantRecord].filter(Boolean),
514
+ waiting_side: waitingSide,
515
+ expected_next_message: nextMessage,
516
+ heartbeat_status: heartbeatStatus,
517
+ operator_projection_metadata: operatorProjectionMetadata,
518
+ missing_surfaces: [...new Set(missingSurfaces)],
519
+ project_continuation: continuation,
520
+ start_work: startWorkResult,
521
+ transfer_packet: normalizedTransferPacket,
522
+ open_channel: openChannel,
523
+ channel_participant: participantRecord,
524
+ heartbeat,
525
+ status_payload: statusPayload,
526
+ };
527
+ }
528
+
529
+ export async function runInterAgentMessagingLoop(client, {
530
+ transferChannelId,
531
+ transfer_channel_id,
532
+ channelId,
533
+ channel_id,
534
+ workTransferPacketId,
535
+ work_transfer_packet_id,
536
+ workflowRunId,
537
+ workflow_run_id,
538
+ participantRole,
539
+ participant_role,
540
+ watchingAgentRole,
541
+ watching_agent_role,
542
+ expectedFromRole,
543
+ expected_from_role,
544
+ expectedMessageKind,
545
+ expected_message_kind,
546
+ expectedNextMessage,
547
+ expected_next_message,
548
+ replyRequest = {},
549
+ reply_request = {},
550
+ replyBodyMarkdown,
551
+ reply_body_markdown,
552
+ replyMessageKind,
553
+ reply_message_kind,
554
+ replyPayload = {},
555
+ reply_payload = {},
556
+ replyScope = {},
557
+ reply_scope = {},
558
+ replyEvidenceRefs = [],
559
+ reply_evidence_refs = [],
560
+ replyRecipientAgentSessionId,
561
+ reply_recipient_agent_session_id,
562
+ replyRecipientActorSessionId,
563
+ reply_recipient_actor_session_id,
564
+ replySenderAgentSessionId,
565
+ reply_sender_agent_session_id,
566
+ replySenderActorSessionId,
567
+ reply_sender_actor_session_id,
568
+ watchRequest = {},
569
+ watch_request = {},
570
+ acknowledgeRequest = {},
571
+ acknowledge_request = {},
572
+ heartbeatRequest = {},
573
+ heartbeat_request = {},
574
+ closeWhenAcknowledged = false,
575
+ close_when_acknowledged = false,
576
+ requestClosure = false,
577
+ request_closure = false,
578
+ closureReason,
579
+ closure_reason,
580
+ failureReason,
581
+ failure_reason,
582
+ closureEvidence = {},
583
+ closure_evidence = {},
584
+ closureEvidenceManifestSha256,
585
+ closure_evidence_manifest_sha256,
586
+ includeTransferChannelProjection = true,
587
+ includeOperatorProjectionMetadata = true,
588
+ metadata = {},
589
+ } = {}) {
590
+ const missingSurfaces = [];
591
+ const normalizedTransferChannelId = cleanText(transfer_channel_id) || cleanText(transferChannelId) || cleanText(channel_id) || cleanText(channelId);
592
+ if (!normalizedTransferChannelId) throw new Error('transfer_channel_id is required.');
593
+ let normalizedPacketId = cleanText(work_transfer_packet_id) || cleanText(workTransferPacketId);
594
+ const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
595
+ const normalizedReplyRequest = isPlainObject(reply_request) ? reply_request : (isPlainObject(replyRequest) ? replyRequest : {});
596
+ const normalizedWatchRequest = isPlainObject(watch_request) ? watch_request : (isPlainObject(watchRequest) ? watchRequest : {});
597
+ const normalizedAcknowledgeRequest = isPlainObject(acknowledge_request) ? acknowledge_request : (isPlainObject(acknowledgeRequest) ? acknowledgeRequest : {});
598
+ const normalizedHeartbeatRequest = isPlainObject(heartbeat_request) ? heartbeat_request : (isPlainObject(heartbeatRequest) ? heartbeatRequest : {});
599
+ const normalizedClosureEvidence = isPlainObject(closure_evidence) ? closure_evidence : (isPlainObject(closureEvidence) ? closureEvidence : {});
600
+ const shouldClose = Boolean(close_when_acknowledged || closeWhenAcknowledged || request_closure || requestClosure);
601
+
602
+ if (!normalizedPacketId) {
603
+ try {
604
+ const status = await client.getCommunicationChannelStatus({ transferChannelId: normalizedTransferChannelId });
605
+ normalizedPacketId = cleanText(status?.packet_id || status?.packetId || status?.work_transfer_packet_id);
606
+ } catch (error) {
607
+ void error;
608
+ }
609
+ }
610
+ if (!normalizedPacketId) {
611
+ throw new Error('work_transfer_packet_id is required.');
612
+ }
613
+
614
+ const resumeSurface = typeof client.resumeTransferChannel === 'function'
615
+ ? client.resumeTransferChannel.bind(client)
616
+ : null;
617
+ if (!resumeSurface) missingSurfaces.push('resumeTransferChannel');
618
+ const resumedChannel = resumeSurface
619
+ ? await resumeSurface({
620
+ transferChannelId: normalizedTransferChannelId,
621
+ workTransferPacketId: normalizedPacketId,
622
+ workflowRunId: cleanText(workflow_run_id) || cleanText(workflowRunId),
623
+ participantRole: cleanText(participant_role) || cleanText(participantRole) || cleanText(watching_agent_role) || cleanText(watchingAgentRole) || 'downstream',
624
+ participantKind: 'agent_session',
625
+ participantLabel: cleanText(participant_role) || cleanText(participantRole) || cleanText(watching_agent_role) || cleanText(watchingAgentRole) || 'downstream',
626
+ agentSessionId: client.agentSessionId,
627
+ currentPhase: 'message_loop',
628
+ currentTaskSummary: cleanText(expected_next_message) || cleanText(expectedNextMessage) || 'Await expected message.',
629
+ lastSeenMessageId: cleanText(normalizedReplyRequest.transfer_channel_message_id) || cleanText(normalizedReplyRequest.transferChannelMessageId),
630
+ metadata: {
631
+ ...normalizedMetadata,
632
+ source: 'runInterAgentMessagingLoop',
633
+ },
634
+ })
635
+ : null;
636
+
637
+ const replySurface = typeof client.replyToTransferChannel === 'function'
638
+ ? client.replyToTransferChannel.bind(client)
639
+ : null;
640
+ if (!replySurface) missingSurfaces.push('replyToTransferChannel');
641
+ const replyBody = normalizedReplyRequest.body_markdown || normalizedReplyRequest.bodyMarkdown || reply_body_markdown || replyBodyMarkdown;
642
+ const shouldReply = Boolean(
643
+ replyBody
644
+ || normalizedReplyRequest.body
645
+ || normalizedReplyRequest.message_kind
646
+ || normalizedReplyRequest.messageKind
647
+ || cleanText(reply_message_kind)
648
+ || cleanText(replyMessageKind)
649
+ || Object.keys(normalizedReplyRequest).length > 0
650
+ );
651
+ const replyResult = shouldReply && replySurface
652
+ ? await replySurface({
653
+ transferChannelId: normalizedTransferChannelId,
654
+ senderAgentSessionId: cleanText(normalizedReplyRequest.sender_agent_session_id) || cleanText(normalizedReplyRequest.senderAgentSessionId) || cleanText(reply_sender_agent_session_id) || cleanText(replySenderAgentSessionId) || client.agentSessionId,
655
+ senderActorSessionId: cleanText(normalizedReplyRequest.sender_actor_session_id) || cleanText(normalizedReplyRequest.senderActorSessionId) || cleanText(reply_sender_actor_session_id) || cleanText(replySenderActorSessionId),
656
+ recipientAgentSessionId: cleanText(normalizedReplyRequest.recipient_agent_session_id) || cleanText(normalizedReplyRequest.recipientAgentSessionId) || cleanText(reply_recipient_agent_session_id) || cleanText(replyRecipientAgentSessionId),
657
+ recipientActorSessionId: cleanText(normalizedReplyRequest.recipient_actor_session_id) || cleanText(normalizedReplyRequest.recipientActorSessionId) || cleanText(reply_recipient_actor_session_id) || cleanText(replyRecipientActorSessionId),
658
+ messageKind: cleanText(normalizedReplyRequest.message_kind) || cleanText(normalizedReplyRequest.messageKind) || cleanText(reply_message_kind) || cleanText(replyMessageKind) || cleanText(expected_next_message) || cleanText(expectedNextMessage) || 'response',
659
+ bodyMarkdown: cleanText(reply_body_markdown) || cleanText(replyBodyMarkdown) || cleanText(normalizedReplyRequest.body_markdown) || cleanText(normalizedReplyRequest.bodyMarkdown) || cleanText(normalizedReplyRequest.body),
660
+ transferChannelMessageStatus: cleanText(normalizedReplyRequest.message_status) || cleanText(normalizedReplyRequest.messageStatus),
661
+ payload: isPlainObject(normalizedReplyRequest.payload) ? normalizedReplyRequest.payload : (isPlainObject(reply_payload) ? reply_payload : (isPlainObject(replyPayload) ? replyPayload : {})),
662
+ scope: isPlainObject(normalizedReplyRequest.scope) ? normalizedReplyRequest.scope : (isPlainObject(reply_scope) ? reply_scope : (isPlainObject(replyScope) ? replyScope : {})),
663
+ requiredResponseSchema: isPlainObject(normalizedReplyRequest.required_response_schema) ? normalizedReplyRequest.required_response_schema : (isPlainObject(normalizedReplyRequest.requiredResponseSchema) ? normalizedReplyRequest.requiredResponseSchema : null),
664
+ evidenceRefs: Array.isArray(normalizedReplyRequest.evidence_refs) ? normalizedReplyRequest.evidence_refs : (Array.isArray(reply_evidence_refs) ? reply_evidence_refs : replyEvidenceRefs),
665
+ metadata: {
666
+ ...normalizedMetadata,
667
+ source: 'runInterAgentMessagingLoop',
668
+ },
669
+ closureReason: cleanText(normalizedReplyRequest.closure_reason) || cleanText(normalizedReplyRequest.closureReason) || cleanText(closure_reason) || cleanText(closureReason),
670
+ failureReason: cleanText(normalizedReplyRequest.failure_reason) || cleanText(normalizedReplyRequest.failureReason) || cleanText(failure_reason) || cleanText(failureReason),
671
+ currentWaitingSide: cleanText(normalizedReplyRequest.current_waiting_side) || cleanText(normalizedReplyRequest.currentWaitingSide),
672
+ })
673
+ : null;
674
+
675
+ const normalizedReplyPayload = isPlainObject(replyResult) ? replyResult : {};
676
+ const messageId = cleanText(normalizedReplyPayload.transfer_channel_message?.transfer_channel_message_id)
677
+ || cleanText(normalizedReplyPayload.communication_message?.transfer_channel_message_id)
678
+ || cleanText(normalizedReplyPayload.communication_message?.agent_message_id)
679
+ || cleanText(normalizedReplyPayload.transfer_channel_message_id)
680
+ || cleanText(normalizedReplyPayload.message_id)
681
+ || cleanText(normalizedReplyPayload.communication_message?.message_id)
682
+ || null;
683
+ const receiptId = cleanText(normalizedReplyPayload.transfer_channel_receipt?.transfer_channel_receipt_id)
684
+ || cleanText(normalizedReplyPayload.transfer_channel_receipt_id)
685
+ || cleanText(normalizedReplyPayload.receipt_id)
686
+ || null;
687
+
688
+ const watchSurface = typeof client.startMessageWatch === 'function'
689
+ ? client.startMessageWatch.bind(client)
690
+ : null;
691
+ if (!watchSurface) missingSurfaces.push('startMessageWatch');
692
+ const startedWatch = watchSurface
693
+ ? await watchSurface({
694
+ transferChannelId: normalizedTransferChannelId,
695
+ workTransferPacketId: normalizedPacketId,
696
+ workflowRunId: cleanText(workflow_run_id) || cleanText(workflowRunId),
697
+ watchingAgentRole: cleanText(watching_agent_role) || cleanText(watchingAgentRole) || 'upstream',
698
+ expectedFromRole: cleanText(expected_from_role) || cleanText(expectedFromRole) || cleanText(participant_role) || cleanText(participantRole) || 'downstream',
699
+ expectedMessageKind: cleanText(expected_message_kind) || cleanText(expectedMessageKind) || cleanText(expected_next_message) || cleanText(expectedNextMessage) || 'response',
700
+ watchType: cleanText(normalizedWatchRequest.watch_type) || cleanText(normalizedWatchRequest.watchType) || 'expected_peer_message',
701
+ watchingAgentSessionId: cleanText(normalizedWatchRequest.watching_agent_session_id) || cleanText(normalizedWatchRequest.watchingAgentSessionId) || client.agentSessionId,
702
+ watchingActorSessionId: cleanText(normalizedWatchRequest.watching_actor_session_id) || cleanText(normalizedWatchRequest.watchingActorSessionId),
703
+ expectedPayload: isPlainObject(normalizedWatchRequest.expected_payload) ? normalizedWatchRequest.expected_payload : (isPlainObject(normalizedWatchRequest.expectedPayload) ? normalizedWatchRequest.expectedPayload : {}),
704
+ lastSeenMessageId: cleanText(normalizedWatchRequest.last_seen_message_id) || cleanText(normalizedWatchRequest.lastSeenMessageId) || messageId,
705
+ lastCheckedAt: normalizedWatchRequest.last_checked_at || normalizedWatchRequest.lastCheckedAt,
706
+ staleAfterSeconds: normalizedWatchRequest.stale_after_seconds ?? normalizedWatchRequest.staleAfterSeconds,
707
+ currentStatus: cleanText(normalizedWatchRequest.current_status) || cleanText(normalizedWatchRequest.currentStatus) || 'watching',
708
+ operatorNudge: cleanText(normalizedWatchRequest.operator_nudge) || cleanText(normalizedWatchRequest.operatorNudge),
709
+ blockedReason: cleanText(normalizedWatchRequest.blocked_reason) || cleanText(normalizedWatchRequest.blockedReason),
710
+ metadata: {
711
+ ...normalizedMetadata,
712
+ source: 'runInterAgentMessagingLoop',
713
+ },
714
+ })
715
+ : null;
716
+
717
+ const watchPayload = isPlainObject(startedWatch) ? startedWatch : {};
718
+ const watchRecord = isPlainObject(watchPayload.message_watch)
719
+ ? watchPayload.message_watch
720
+ : (isPlainObject(watchPayload.messageWatch) ? watchPayload.messageWatch : watchPayload);
721
+ const watchId = cleanText(watchRecord.message_watch_id)
722
+ || cleanText(watchRecord.watch_id)
723
+ || null;
724
+ const observedMessageId = messageId || cleanText(normalizedAcknowledgeRequest.observed_message_id) || cleanText(normalizedAcknowledgeRequest.observedMessageId);
725
+ const ackSurface = typeof client.acknowledgeExpectedMessage === 'function'
726
+ ? client.acknowledgeExpectedMessage.bind(client)
727
+ : null;
728
+ if (!ackSurface) missingSurfaces.push('acknowledgeExpectedMessage');
729
+ const acknowledgedResult = ackSurface
730
+ ? await ackSurface({
731
+ transferChannelId: normalizedTransferChannelId,
732
+ messageWatchId: watchId || cleanText(normalizedAcknowledgeRequest.message_watch_id) || cleanText(normalizedAcknowledgeRequest.messageWatchId) || cleanText(normalizedAcknowledgeRequest.watch_id) || cleanText(normalizedAcknowledgeRequest.watchId),
733
+ observedMessageId,
734
+ observedMessageKind: cleanText(normalizedAcknowledgeRequest.observed_message_kind) || cleanText(normalizedAcknowledgeRequest.observedMessageKind) || cleanText(expected_next_message) || cleanText(expectedNextMessage),
735
+ lastCheckedAt: normalizedAcknowledgeRequest.last_checked_at || normalizedAcknowledgeRequest.lastCheckedAt,
736
+ operatorNudge: cleanText(normalizedAcknowledgeRequest.operator_nudge) || cleanText(normalizedAcknowledgeRequest.operatorNudge),
737
+ metadata: {
738
+ ...normalizedMetadata,
739
+ source: 'runInterAgentMessagingLoop',
740
+ },
741
+ })
742
+ : null;
743
+ const acknowledged = Boolean(acknowledgedResult);
744
+
745
+ const heartbeatSurface = typeof client.postCollaborationHeartbeat === 'function'
746
+ ? client.postCollaborationHeartbeat.bind(client)
747
+ : typeof client.postAgentHeartbeat === 'function'
748
+ ? client.postAgentHeartbeat.bind(client)
749
+ : null;
750
+ if (!heartbeatSurface) missingSurfaces.push('postCollaborationHeartbeat');
751
+ const heartbeatResult = heartbeatSurface
752
+ ? await heartbeatSurface({
753
+ transferChannelId: normalizedTransferChannelId,
754
+ workTransferPacketId: normalizedPacketId,
755
+ workflowRunId: cleanText(workflow_run_id) || cleanText(workflowRunId),
756
+ participantRole: cleanText(participant_role) || cleanText(participantRole) || cleanText(watching_agent_role) || cleanText(watchingAgentRole) || 'downstream',
757
+ activityState: 'active',
758
+ agentSessionId: client.agentSessionId,
759
+ actorSessionId: cleanText(normalizedHeartbeatRequest.actor_session_id) || cleanText(normalizedHeartbeatRequest.actorSessionId),
760
+ currentPhase: cleanText(normalizedHeartbeatRequest.current_phase) || cleanText(normalizedHeartbeatRequest.currentPhase) || 'message_loop',
761
+ currentTaskSummary: cleanText(normalizedHeartbeatRequest.current_task_summary) || cleanText(normalizedHeartbeatRequest.currentTaskSummary) || cleanText(expected_next_message) || cleanText(expectedNextMessage) || 'Await expected message.',
762
+ isActive: normalizedHeartbeatRequest.is_active ?? normalizedHeartbeatRequest.isActive ?? true,
763
+ observedAt: normalizedHeartbeatRequest.observed_at || normalizedHeartbeatRequest.observedAt,
764
+ metadata: {
765
+ ...normalizedMetadata,
766
+ source: 'runInterAgentMessagingLoop',
767
+ },
768
+ })
769
+ : null;
770
+
771
+ const transferChannelProjectionSurface = typeof client.getTransferChannelProjection === 'function'
772
+ ? client.getTransferChannelProjection.bind(client)
773
+ : typeof client.getLogaTransferChannelThreadProjection === 'function'
774
+ ? client.getLogaTransferChannelThreadProjection.bind(client)
775
+ : null;
776
+ if (!transferChannelProjectionSurface) missingSurfaces.push('getTransferChannelProjection');
777
+ const transferChannelProjection = includeTransferChannelProjection && transferChannelProjectionSurface
778
+ ? await transferChannelProjectionSurface(normalizedTransferChannelId).catch(() => null)
779
+ : null;
780
+
781
+ const closureRequested = Boolean(shouldClose && acknowledged);
782
+ const closeSurface = typeof client.closeTransferChannel === 'function'
783
+ ? client.closeTransferChannel.bind(client)
784
+ : null;
785
+ if (!closeSurface) missingSurfaces.push('closeTransferChannel');
786
+ const closeResult = closureRequested && closeSurface
787
+ ? await closeSurface({
788
+ transferChannelId: normalizedTransferChannelId,
789
+ closureStatus: 'closed',
790
+ closureReason: cleanText(closure_reason) || cleanText(closureReason) || 'expected message loop completed',
791
+ failureReason: cleanText(failure_reason) || cleanText(failureReason),
792
+ evidence: normalizedClosureEvidence,
793
+ evidenceManifestSha256: cleanText(closure_evidence_manifest_sha256) || cleanText(closureEvidenceManifestSha256),
794
+ closedByAgentSessionId: client.agentSessionId,
795
+ metadata: {
796
+ ...normalizedMetadata,
797
+ source: 'runInterAgentMessagingLoop',
798
+ },
799
+ })
800
+ : null;
801
+
802
+ const closePayload = isPlainObject(closeResult) ? closeResult : {};
803
+ const channelClosed = Boolean(
804
+ closePayload.closed
805
+ || closePayload.closed_channel
806
+ || closePayload.channel_closed
807
+ || closePayload.closed_at
808
+ || closePayload.transfer_channel?.channel_status === 'closed'
809
+ || closePayload.communication_transfer_channel?.channel_status === 'closed'
810
+ );
811
+ const statusPayload = {
812
+ transfer_channel: resumedChannel || null,
813
+ watch: startedWatch || null,
814
+ reply: replyResult || null,
815
+ acknowledge: acknowledgedResult || null,
816
+ heartbeat: heartbeatResult || null,
817
+ close: closeResult || null,
818
+ transfer_channel_projection: transferChannelProjection,
819
+ };
820
+
821
+ const waitingSide = cleanText(
822
+ watchRecord.waiting_side
823
+ || watchRecord.expected_from_role
824
+ || watchRecord.expectedFromRole
825
+ || normalizedReplyPayload.current_waiting_side
826
+ || normalizedReplyPayload.currentWaitingSide
827
+ || normalizedReplyPayload.waiting_side
828
+ || normalizedReplyPayload.waitingSide
829
+ || heartbeatResult?.waiting_side
830
+ || resumedChannel?.waiting_side
831
+ ) || null;
832
+ const expectedNext = cleanText(
833
+ watchRecord.expected_message_kind
834
+ || watchRecord.expectedMessageKind
835
+ || normalizedReplyPayload.next_message_kind
836
+ || normalizedReplyPayload.expected_next_message
837
+ || normalizedReplyPayload.expectedNextMessage
838
+ || heartbeatResult?.expected_message_kind
839
+ || expected_message_kind
840
+ || expectedMessageKind
841
+ || expected_next_message
842
+ || expectedNextMessage
843
+ ) || null;
844
+ const heartbeatStatus = cleanText(
845
+ heartbeatResult?.collaboration_heartbeat?.activity_state
846
+ || heartbeatResult?.activity_state
847
+ || heartbeatResult?.status
848
+ || heartbeatResult?.heartbeat_status
849
+ ) || (heartbeatResult ? 'active' : 'missing');
850
+
851
+ const operatorProjectionMetadata = includeOperatorProjectionMetadata
852
+ ? {
853
+ transfer_channel_projection: transferChannelProjection,
854
+ watch: startedWatch,
855
+ reply: replyResult,
856
+ acknowledge: acknowledgedResult,
857
+ heartbeat: heartbeatResult,
858
+ close: closeResult,
859
+ }
860
+ : null;
861
+
862
+ const established = Boolean(normalizedTransferChannelId && normalizedPacketId);
863
+ return {
864
+ status: missingSurfaces.length > 0 ? 'partial' : channelClosed ? 'closed' : established ? 'waiting' : 'blocked',
865
+ channel_id: normalizedTransferChannelId,
866
+ message_id: messageId || null,
867
+ receipt_id: receiptId || null,
868
+ watch_id: watchId || null,
869
+ waiting_side: waitingSide,
870
+ expected_next_message: expectedNext,
871
+ acknowledged,
872
+ heartbeat_status: heartbeatStatus,
873
+ closure_requested: closureRequested,
874
+ channel_closed: channelClosed,
875
+ operator_projection_metadata: operatorProjectionMetadata,
876
+ missing_surfaces: [...new Set(missingSurfaces)],
877
+ transfer_channel: resumedChannel,
878
+ message_watch: startedWatch,
879
+ reply: replyResult,
880
+ acknowledge: acknowledgedResult,
881
+ heartbeat: heartbeatResult,
882
+ close: closeResult,
883
+ status_payload: statusPayload,
884
+ };
885
+ }