@xmoxmo/bncr 0.3.5 → 0.3.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.
Files changed (164) hide show
  1. package/README.md +5 -0
  2. package/dist/index.js +28 -5
  3. package/index.ts +55 -721
  4. package/package.json +8 -4
  5. package/scripts/check-pack.mjs +93 -18
  6. package/scripts/check-register-drift.mjs +35 -13
  7. package/scripts/selfcheck.mjs +80 -11
  8. package/src/bootstrap/channel-plugin-runtime.ts +81 -0
  9. package/src/bootstrap/cli.ts +97 -0
  10. package/src/bootstrap/register-runtime-gateway.ts +129 -0
  11. package/src/bootstrap/register-runtime-helpers.ts +140 -0
  12. package/src/bootstrap/register-runtime-singleton.ts +137 -0
  13. package/src/bootstrap/register-runtime.ts +201 -0
  14. package/src/bootstrap/runtime-discovery.ts +187 -0
  15. package/src/bootstrap/runtime-loader.ts +54 -0
  16. package/src/channel.ts +1590 -4967
  17. package/src/core/accounts.ts +23 -4
  18. package/src/core/dead-letter-diagnostics.ts +37 -5
  19. package/src/core/diagnostics.ts +31 -15
  20. package/src/core/downlink-health.ts +3 -11
  21. package/src/core/extended-diagnostics.ts +78 -36
  22. package/src/core/file-transfer-payloads.ts +1 -1
  23. package/src/core/logging.ts +1 -0
  24. package/src/core/outbox-enqueue.ts +13 -2
  25. package/src/core/outbox-entry-builders.ts +2 -0
  26. package/src/core/outbox-summary.ts +75 -3
  27. package/src/core/permissions.ts +15 -2
  28. package/src/core/persisted-outbox-entry.ts +21 -6
  29. package/src/core/policy.ts +45 -4
  30. package/src/core/probe.ts +3 -15
  31. package/src/core/register-trace.ts +3 -3
  32. package/src/core/status.ts +43 -4
  33. package/src/core/targets.ts +216 -205
  34. package/src/core/types.ts +221 -0
  35. package/src/core/value-sanitize.ts +29 -0
  36. package/src/messaging/inbound/commands.ts +147 -172
  37. package/src/messaging/inbound/context-facts.ts +4 -2
  38. package/src/messaging/inbound/contracts.ts +70 -0
  39. package/src/messaging/inbound/dispatch-prep.ts +303 -0
  40. package/src/messaging/inbound/dispatch.ts +49 -462
  41. package/src/messaging/inbound/gate.ts +18 -5
  42. package/src/messaging/inbound/last-route.ts +10 -4
  43. package/src/messaging/inbound/media-url-download.ts +109 -0
  44. package/src/messaging/inbound/native-command-runtime.ts +225 -0
  45. package/src/messaging/inbound/parse.ts +2 -1
  46. package/src/messaging/inbound/remote-media.ts +49 -0
  47. package/src/messaging/inbound/reply-config.ts +16 -4
  48. package/src/messaging/inbound/reply-dispatch.ts +162 -0
  49. package/src/messaging/inbound/runtime-compat.ts +31 -10
  50. package/src/messaging/inbound/session-label.ts +15 -7
  51. package/src/messaging/inbound/turn-context.ts +131 -0
  52. package/src/messaging/outbound/actions.ts +24 -10
  53. package/src/messaging/outbound/diagnostics-debug-builders.ts +365 -0
  54. package/src/messaging/outbound/diagnostics.ts +31 -355
  55. package/src/messaging/outbound/durable-message-adapter.ts +20 -16
  56. package/src/messaging/outbound/durable-queue-adapter.ts +20 -7
  57. package/src/messaging/outbound/media.ts +24 -13
  58. package/src/messaging/outbound/reply-enqueue-media.ts +181 -0
  59. package/src/messaging/outbound/reply-enqueue.ts +57 -134
  60. package/src/messaging/outbound/send-params.ts +3 -0
  61. package/src/messaging/outbound/send.ts +19 -10
  62. package/src/messaging/outbound/session-route.ts +18 -3
  63. package/src/openclaw/channel-runtime-contracts.ts +76 -0
  64. package/src/openclaw/config-runtime.ts +13 -7
  65. package/src/openclaw/inbound-session-runtime.ts +7 -3
  66. package/src/openclaw/ingress-runtime.ts +17 -27
  67. package/src/openclaw/reply-runtime.ts +54 -59
  68. package/src/openclaw/routing-runtime.ts +35 -18
  69. package/src/openclaw/runtime-surface.ts +156 -12
  70. package/src/openclaw/sdk-helpers.ts +8 -1
  71. package/src/openclaw/session-route-runtime.ts +12 -12
  72. package/src/plugin/ack-outbox-runtime-group.ts +264 -0
  73. package/src/plugin/bridge-ack-facade.ts +137 -0
  74. package/src/plugin/bridge-connection-facade.ts +111 -0
  75. package/src/plugin/bridge-diagnostics-facade.ts +23 -0
  76. package/src/plugin/bridge-drain-facade.ts +98 -0
  77. package/src/plugin/bridge-extended-diagnostics-facade.ts +149 -0
  78. package/src/plugin/bridge-file-transfer-push-facade.ts +140 -0
  79. package/src/plugin/bridge-lifecycle.ts +156 -0
  80. package/src/plugin/bridge-media-facade.ts +241 -0
  81. package/src/plugin/bridge-outbox-facade.ts +182 -0
  82. package/src/plugin/bridge-runtime-helpers.ts +266 -0
  83. package/src/plugin/bridge-runtime-snapshots.ts +104 -0
  84. package/src/plugin/bridge-runtime-surface-facade.ts +8 -0
  85. package/src/plugin/bridge-status-facade.ts +76 -0
  86. package/src/plugin/bridge-status-worker-facade.ts +72 -0
  87. package/src/plugin/bridge-support-runtime.ts +137 -0
  88. package/src/plugin/bridge-surface-handlers-group.ts +242 -0
  89. package/src/plugin/bridge-surface-helpers.ts +28 -0
  90. package/src/plugin/capabilities.ts +1 -3
  91. package/src/plugin/channel-components.ts +289 -0
  92. package/src/plugin/channel-inbound-helpers.ts +149 -0
  93. package/src/plugin/channel-plugin-bridge-group.ts +129 -0
  94. package/src/plugin/channel-plugin-surface-group.ts +202 -0
  95. package/src/plugin/channel-runtime-builders-delivery.ts +513 -0
  96. package/src/plugin/channel-runtime-builders-status.ts +331 -0
  97. package/src/plugin/channel-runtime-builders.ts +25 -0
  98. package/src/plugin/channel-runtime-constants.ts +40 -0
  99. package/src/plugin/channel-runtime-types.ts +146 -0
  100. package/src/plugin/channel-send-runtime-group.ts +37 -0
  101. package/src/plugin/channel-send.ts +226 -0
  102. package/src/plugin/channel-utils.ts +102 -0
  103. package/src/plugin/config.ts +24 -3
  104. package/src/plugin/connection-handlers-helpers.ts +254 -0
  105. package/src/plugin/connection-handlers.ts +440 -0
  106. package/src/plugin/connection-state-helpers.ts +159 -0
  107. package/src/plugin/connection-state-runtime-group.ts +51 -0
  108. package/src/plugin/connection-state.ts +527 -0
  109. package/src/plugin/diagnostics-handlers.ts +211 -0
  110. package/src/plugin/error-message.ts +15 -0
  111. package/src/plugin/file-ack-runtime.ts +284 -0
  112. package/src/plugin/file-inbound-abort.ts +112 -0
  113. package/src/plugin/file-inbound-chunk.ts +146 -0
  114. package/src/plugin/file-inbound-complete.ts +153 -0
  115. package/src/plugin/file-inbound-handlers.ts +19 -0
  116. package/src/plugin/file-inbound-init.ts +122 -0
  117. package/src/plugin/file-inbound-runtime.ts +51 -0
  118. package/src/plugin/file-inbound-state.ts +62 -0
  119. package/src/plugin/file-transfer-logs.ts +227 -0
  120. package/src/plugin/file-transfer-orchestrator-chunk.ts +135 -0
  121. package/src/plugin/file-transfer-orchestrator.ts +304 -0
  122. package/src/plugin/file-transfer-runtime-group.ts +102 -0
  123. package/src/plugin/file-transfer-send.ts +89 -0
  124. package/src/plugin/file-transfer-setup.ts +206 -0
  125. package/src/plugin/gateway-event-context.ts +41 -0
  126. package/src/plugin/gateway-runtime.ts +14 -4
  127. package/src/plugin/inbound-acceptance.ts +107 -0
  128. package/src/plugin/inbound-handlers.ts +248 -0
  129. package/src/plugin/inbound-surface-handlers-group.ts +152 -0
  130. package/src/plugin/media-dedupe-runtime.ts +90 -0
  131. package/src/plugin/media-orchestrators-runtime-group.ts +316 -0
  132. package/src/plugin/message-ack-runtime.ts +284 -0
  133. package/src/plugin/message-send.ts +16 -6
  134. package/src/plugin/messaging.ts +98 -36
  135. package/src/plugin/outbound.ts +50 -8
  136. package/src/plugin/outbox-ack-logs.ts +136 -0
  137. package/src/plugin/outbox-ack-outcome.ts +128 -0
  138. package/src/plugin/outbox-drain-ack.ts +145 -0
  139. package/src/plugin/outbox-drain-failure.ts +84 -0
  140. package/src/plugin/outbox-drain-loop.ts +554 -0
  141. package/src/plugin/outbox-drain-post-push.ts +159 -0
  142. package/src/plugin/outbox-drain-runtime.ts +141 -0
  143. package/src/plugin/outbox-drain-schedule.ts +116 -0
  144. package/src/plugin/outbox-file-push-flow.ts +69 -0
  145. package/src/plugin/outbox-push-route-runtime-group.ts +81 -0
  146. package/src/plugin/outbox-push.ts +267 -0
  147. package/src/plugin/outbox-route.ts +181 -0
  148. package/src/plugin/outbox-text-push-flow.ts +90 -0
  149. package/src/plugin/runtime-diagnostics-assembler.ts +183 -0
  150. package/src/plugin/runtime-diagnostics-helpers.ts +302 -0
  151. package/src/plugin/runtime-diagnostics-payload-builders.ts +171 -0
  152. package/src/plugin/runtime-diagnostics-snapshot.ts +31 -0
  153. package/src/plugin/setup.ts +33 -6
  154. package/src/plugin/state-store.ts +249 -0
  155. package/src/plugin/state-transient-runtime-group.ts +105 -0
  156. package/src/plugin/status-runtime.ts +251 -0
  157. package/src/plugin/status.ts +33 -7
  158. package/src/plugin/target-runtime.ts +141 -0
  159. package/src/plugin/target-status-runtime-group.ts +130 -0
  160. package/src/plugin/transient-state-runtime.ts +82 -0
  161. package/src/runtime/outbound-ack-timeout.ts +5 -3
  162. package/src/runtime/outbound-flags.ts +24 -8
  163. package/src/runtime/status-snapshots.ts +36 -7
  164. package/src/runtime/status-worker.ts +34 -4
@@ -1,6 +1,24 @@
1
- import type { BncrConnection, OutboxEntry } from '../../core/types.ts';
2
- import { OUTBOUND_TERMINAL_REASON, type OutboundScheduleSource } from './reasons.ts';
3
- import type { RetryRerouteDecision } from './retry-policy.ts';
1
+ import type {
2
+ BncrAckObservability,
3
+ BncrExtendedOutboundDiagnostics,
4
+ BncrOutboxIncidentSummary,
5
+ BncrOutboxQueueDiagnostics,
6
+ OutboxEntry,
7
+ } from '../../core/types.ts';
8
+
9
+ export {
10
+ buildFlushDebugInfo,
11
+ buildOutboxAckDebugInfo,
12
+ buildOutboxDrainSkipDebugInfo,
13
+ buildOutboxDrainStuckDebugInfo,
14
+ buildOutboxPushOkDebugInfo,
15
+ buildOutboxPushSkipDebugInfo,
16
+ buildOutboxRouteSelectDebugInfo,
17
+ buildOutboxScheduleDebugInfo,
18
+ buildPushFailureDebugInfo,
19
+ buildReplyMediaFallbackDebugInfo,
20
+ buildRetryRerouteDebugInfo,
21
+ } from './diagnostics-debug-builders.ts';
4
22
 
5
23
  type OutboxIncidentSummaryInput = {
6
24
  pending: number;
@@ -36,7 +54,9 @@ function positiveAgeMs(nowMs: number, at: unknown): number | null {
36
54
  return Math.max(0, nowMs - n);
37
55
  }
38
56
 
39
- export function buildOutboxIncidentSummary(input: OutboxIncidentSummaryInput) {
57
+ export function buildOutboxIncidentSummary(
58
+ input: OutboxIncidentSummaryInput,
59
+ ): BncrOutboxIncidentSummary {
40
60
  const pending = Math.max(0, Math.floor(finiteNumberOrNull(input.pending) || 0));
41
61
  const nowMs = finiteNumberOrNull(input.nowMs) || Date.now();
42
62
  const lastPushError =
@@ -134,7 +154,7 @@ export function buildOutboxIncidentSummary(input: OutboxIncidentSummaryInput) {
134
154
  }
135
155
 
136
156
  export function buildExtendedOutboundDiagnostics(input: {
137
- outbox: Record<string, any>;
157
+ outbox: BncrOutboxQueueDiagnostics;
138
158
  enqueueCount: number;
139
159
  lastEnqueueAt?: number | null;
140
160
  prePushGuardSkipCount: number;
@@ -142,9 +162,9 @@ export function buildExtendedOutboundDiagnostics(input: {
142
162
  lastPrePushGuardSkipReason?: string | null;
143
163
  hasGatewayContext: boolean;
144
164
  lastGatewayContextAt?: number | null;
145
- ackObservability: Record<string, any>;
165
+ ackObservability: BncrAckObservability;
146
166
  nowMs?: number;
147
- }) {
167
+ }): BncrExtendedOutboundDiagnostics {
148
168
  const lastEnqueueAt = finiteNumberOrNull(input.lastEnqueueAt);
149
169
  const lastPrePushGuardSkipAt = finiteNumberOrNull(input.lastPrePushGuardSkipAt);
150
170
  const lastGatewayContextAt = finiteNumberOrNull(input.lastGatewayContextAt);
@@ -159,7 +179,10 @@ export function buildExtendedOutboundDiagnostics(input: {
159
179
  lastGatewayContextAt,
160
180
  incident: buildOutboxIncidentSummary({
161
181
  ...input.outbox,
182
+ pending: Number(input.outbox.pending) || 0,
162
183
  hasGatewayContext: input.hasGatewayContext,
184
+ activeOutboundConnection: Boolean(input.outbox.activeOutboundConnection),
185
+ activeOutboundConnectionCount: Number(input.outbox.activeOutboundConnectionCount) || 0,
163
186
  prePushGuardSkipCount: input.prePushGuardSkipCount,
164
187
  lastPrePushGuardSkipAt,
165
188
  lastPrePushGuardSkipReason: input.lastPrePushGuardSkipReason || null,
@@ -180,7 +203,7 @@ export function buildOutboxQueueDiagnostics(args: {
180
203
  outboxEntries: Iterable<OutboxEntry>;
181
204
  pendingAllAccounts: number;
182
205
  pushConnIds: Iterable<string>;
183
- }) {
206
+ }): BncrOutboxQueueDiagnostics {
184
207
  const accountEntries = Array.from(args.outboxEntries).filter(
185
208
  (entry) => entry.accountId === args.accountId,
186
209
  );
@@ -221,338 +244,6 @@ export function buildOutboxQueueDiagnostics(args: {
221
244
  };
222
245
  }
223
246
 
224
- export function buildOutboxScheduleDebugInfo(args: {
225
- bridgeId: string;
226
- accountId?: string | null;
227
- // Account-local aggregated next delay after considering currently visible entries.
228
- localNextDelay?: number | null;
229
- // Bridge-global aggregated next delay after merging account-local scheduling decisions.
230
- globalNextDelay?: number | null;
231
- // Immediate wait observed for this specific scheduling event.
232
- wait?: number | null;
233
- source: OutboundScheduleSource;
234
- messageId?: string;
235
- }) {
236
- return {
237
- bridge: args.bridgeId,
238
- ...(args.accountId ? { accountId: args.accountId } : {}),
239
- ...(args.messageId ? { messageId: args.messageId } : {}),
240
- source: args.source,
241
- ...(typeof args.wait === 'number' ? { wait: args.wait } : {}),
242
- ...(typeof args.localNextDelay === 'number' ? { localNextDelay: args.localNextDelay } : {}),
243
- ...(typeof args.globalNextDelay === 'number' ? { globalNextDelay: args.globalNextDelay } : {}),
244
- };
245
- }
246
-
247
- export function buildOutboxPushSkipDebugInfo(args: {
248
- messageId: string;
249
- accountId: string;
250
- reason: string;
251
- recentInboundReachable?: boolean;
252
- kind?: string;
253
- routeReason?: string;
254
- connIds?: Iterable<string>;
255
- ownerConnId?: string;
256
- ownerClientId?: string;
257
- activeConnectionCount?: number;
258
- connections?: Iterable<BncrConnection>;
259
- }) {
260
- return {
261
- messageId: args.messageId,
262
- accountId: args.accountId,
263
- ...(args.kind ? { kind: args.kind } : {}),
264
- reason: args.reason,
265
- ...(args.routeReason ? { routeReason: args.routeReason } : {}),
266
- ...(args.connIds ? { connIds: Array.from(args.connIds) } : {}),
267
- ...(args.ownerConnId ? { ownerConnId: args.ownerConnId } : {}),
268
- ...(args.ownerClientId ? { ownerClientId: args.ownerClientId } : {}),
269
- ...(typeof args.activeConnectionCount === 'number'
270
- ? { activeConnectionCount: args.activeConnectionCount }
271
- : {}),
272
- ...(args.connections
273
- ? {
274
- connections: Array.from(args.connections)
275
- .filter((c) => c.accountId === args.accountId)
276
- .slice(0, 8)
277
- .map((c) => ({
278
- connId: c.connId,
279
- clientId: c.clientId,
280
- lastSeenAt: c.lastSeenAt,
281
- outboundReadyUntil: (c as any).outboundReadyUntil,
282
- preferredForOutboundUntil: (c as any).preferredForOutboundUntil,
283
- inboundOnly: (c as any).inboundOnly,
284
- lastAckOkAt: (c as any).lastAckOkAt,
285
- lastPushTimeoutAt: (c as any).lastPushTimeoutAt,
286
- pushFailureScore: (c as any).pushFailureScore,
287
- })),
288
- }
289
- : {}),
290
- ...(typeof args.recentInboundReachable === 'boolean'
291
- ? { recentInboundReachable: args.recentInboundReachable }
292
- : {}),
293
- };
294
- }
295
-
296
- export function buildOutboxRouteSelectDebugInfo(args: {
297
- messageId: string;
298
- accountId: string;
299
- routeReason: string;
300
- connIds: Iterable<string>;
301
- ownerConnId?: string;
302
- ownerClientId?: string;
303
- recentInboundReachable: boolean;
304
- event: string;
305
- kind?: string;
306
- }) {
307
- return {
308
- messageId: args.messageId,
309
- accountId: args.accountId,
310
- ...(args.kind ? { kind: args.kind } : {}),
311
- routeReason: args.routeReason,
312
- connIds: Array.from(args.connIds),
313
- ownerConnId: args.ownerConnId || '',
314
- ownerClientId: args.ownerClientId || '',
315
- recentInboundReachable: args.recentInboundReachable,
316
- event: args.event,
317
- };
318
- }
319
-
320
- export function buildOutboxPushOkDebugInfo(args: {
321
- messageId: string;
322
- accountId: string;
323
- connIds: Iterable<string>;
324
- ownerConnId?: string;
325
- ownerClientId?: string;
326
- recentInboundReachable: boolean;
327
- event: string;
328
- kind?: string;
329
- }) {
330
- return {
331
- messageId: args.messageId,
332
- accountId: args.accountId,
333
- ...(args.kind ? { kind: args.kind } : {}),
334
- connIds: Array.from(args.connIds),
335
- ownerConnId: args.ownerConnId || '',
336
- ownerClientId: args.ownerClientId || '',
337
- recentInboundReachable: args.recentInboundReachable,
338
- event: args.event,
339
- };
340
- }
341
-
342
- export function buildFlushDebugInfo(args: {
343
- bridgeId: string;
344
- accountId: string | null;
345
- targetAccounts: string[];
346
- outboxSize: number;
347
- trigger: string;
348
- reason?: string;
349
- }) {
350
- return {
351
- bridge: args.bridgeId,
352
- accountId: args.accountId,
353
- targetAccounts: [...args.targetAccounts],
354
- outboxSize: args.outboxSize,
355
- trigger: args.trigger,
356
- reason: args.reason,
357
- };
358
- }
359
-
360
- export function buildOutboxDrainSkipDebugInfo(args: {
361
- bridgeId: string;
362
- accountId: string;
363
- reason: string;
364
- outboxSize: number;
365
- trigger: string;
366
- }) {
367
- return {
368
- bridge: args.bridgeId,
369
- accountId: args.accountId,
370
- reason: args.reason,
371
- outboxSize: args.outboxSize,
372
- trigger: args.trigger,
373
- };
374
- }
375
-
376
- export function buildOutboxDrainStuckDebugInfo(args: {
377
- bridgeId: string;
378
- accountId: string;
379
- reason: string;
380
- trigger: string;
381
- outboxSize: number;
382
- pending: number;
383
- runningMs: number;
384
- runningSince?: number | null;
385
- hasGatewayContext: boolean;
386
- activeConnectionCount: number;
387
- messageAckWaiters: number;
388
- fileAckWaiters: number;
389
- pendingEntries?: Iterable<{
390
- messageId?: string;
391
- retryCount?: number;
392
- nextAttemptAt?: number;
393
- lastAttemptAt?: number;
394
- lastError?: string;
395
- lastPushAt?: number;
396
- lastPushConnId?: string;
397
- routeAttemptConnIds?: string[];
398
- }>;
399
- connections?: Iterable<BncrConnection>;
400
- }) {
401
- return {
402
- bridge: args.bridgeId,
403
- accountId: args.accountId,
404
- reason: args.reason,
405
- trigger: args.trigger,
406
- outboxSize: args.outboxSize,
407
- pending: args.pending,
408
- runningMs: args.runningMs,
409
- runningSince: args.runningSince ?? null,
410
- hasGatewayContext: args.hasGatewayContext,
411
- activeConnectionCount: args.activeConnectionCount,
412
- waiters: {
413
- messageAck: args.messageAckWaiters,
414
- fileAck: args.fileAckWaiters,
415
- },
416
- ...(args.pendingEntries
417
- ? {
418
- pendingEntries: Array.from(args.pendingEntries)
419
- .slice(0, 8)
420
- .map((entry) => ({
421
- messageId: entry.messageId || '',
422
- retryCount: entry.retryCount,
423
- nextAttemptAt: entry.nextAttemptAt,
424
- lastAttemptAt: entry.lastAttemptAt,
425
- lastError: entry.lastError,
426
- lastPushAt: entry.lastPushAt,
427
- lastPushConnId: entry.lastPushConnId,
428
- routeAttemptConnIds: entry.routeAttemptConnIds,
429
- })),
430
- }
431
- : {}),
432
- ...(args.connections
433
- ? {
434
- connections: Array.from(args.connections)
435
- .filter((c) => c.accountId === args.accountId)
436
- .slice(0, 8)
437
- .map((c) => ({
438
- connId: c.connId,
439
- clientId: c.clientId,
440
- connectedAt: c.connectedAt,
441
- lastSeenAt: c.lastSeenAt,
442
- outboundReadyUntil: (c as any).outboundReadyUntil,
443
- preferredForOutboundUntil: (c as any).preferredForOutboundUntil,
444
- inboundOnly: (c as any).inboundOnly,
445
- lastAckOkAt: (c as any).lastAckOkAt,
446
- lastPushTimeoutAt: (c as any).lastPushTimeoutAt,
447
- pushFailureScore: (c as any).pushFailureScore,
448
- })),
449
- }
450
- : {}),
451
- };
452
- }
453
-
454
- export function buildOutboxAckDebugInfo(args: {
455
- messageId: string;
456
- accountId: string;
457
- requireAck: boolean;
458
- ackResult: 'acked' | 'timeout';
459
- onlineNow: boolean;
460
- recentInboundReachable: boolean;
461
- connIds?: Iterable<string>;
462
- ownerConnId?: string;
463
- ownerClientId?: string;
464
- sessionKey?: string;
465
- to?: string;
466
- ackStage?: string;
467
- ackOutcome?: string;
468
- reason?: string;
469
- kind?: string;
470
- event?: string;
471
- ackTimeoutMs?: number;
472
- adaptiveAckTimeoutEnabled?: boolean;
473
- }) {
474
- return {
475
- messageId: args.messageId,
476
- accountId: args.accountId,
477
- ...(args.sessionKey ? { sessionKey: args.sessionKey } : {}),
478
- ...(args.to ? { to: args.to } : {}),
479
- ...(args.kind ? { kind: args.kind } : {}),
480
- requireAck: args.requireAck,
481
- ackResult: args.ackResult,
482
- ackStage: args.ackStage || 'message',
483
- ackOutcome: args.ackOutcome || args.ackResult,
484
- ...(args.reason ? { reason: args.reason } : {}),
485
- ...(typeof args.ackTimeoutMs === 'number' ? { ackTimeoutMs: args.ackTimeoutMs } : {}),
486
- ...(typeof args.adaptiveAckTimeoutEnabled === 'boolean'
487
- ? { adaptiveAckTimeoutEnabled: args.adaptiveAckTimeoutEnabled }
488
- : {}),
489
- onlineNow: args.onlineNow,
490
- recentInboundReachable: args.recentInboundReachable,
491
- ...(args.connIds ? { connIds: Array.from(args.connIds) } : {}),
492
- ...(args.ownerConnId ? { ownerConnId: args.ownerConnId } : {}),
493
- ...(args.ownerClientId ? { ownerClientId: args.ownerClientId } : {}),
494
- ...(args.event ? { event: args.event } : {}),
495
- };
496
- }
497
-
498
- export function buildRetryRerouteDebugInfo(args: {
499
- messageId: string;
500
- accountId: string;
501
- currentConnId: string;
502
- decision: RetryRerouteDecision;
503
- availableConnIds: string[];
504
- }) {
505
- if (args.decision.kind !== 'retry') {
506
- return {
507
- messageId: args.messageId,
508
- accountId: args.accountId,
509
- currentConnId: args.currentConnId,
510
- availableConnIds: [...args.availableConnIds],
511
- kind: args.decision.kind,
512
- terminalReason: args.decision.terminalReason,
513
- nextRetryCount: args.decision.nextRetryCount,
514
- lastAttemptAt: args.decision.lastAttemptAt,
515
- };
516
- }
517
-
518
- return {
519
- messageId: args.messageId,
520
- accountId: args.accountId,
521
- currentConnId: args.currentConnId,
522
- attemptedConnIds: [...args.decision.attemptedConnIds],
523
- availableConnIds: [...args.availableConnIds],
524
- revalidatedConnIds: [...args.decision.revalidatedConnIds],
525
- hasUntriedAlternative: args.decision.hasUntriedAlternative,
526
- shouldFastReroute: args.decision.shouldFastReroute,
527
- routeAttemptRound: args.decision.routeAttemptRound,
528
- nextAttemptAt: args.decision.nextAttemptAt,
529
- fastReroutePending: args.decision.fastReroutePending,
530
- nextRetryCount: args.decision.nextRetryCount,
531
- lastAttemptAt: args.decision.lastAttemptAt,
532
- lastError: args.decision.lastError,
533
- kind: args.decision.kind,
534
- };
535
- }
536
-
537
- export function buildPushFailureDebugInfo(args: {
538
- messageId: string;
539
- accountId: string;
540
- retryCount: number;
541
- lastError?: string;
542
- retryable?: boolean;
543
- kind?: string;
544
- }) {
545
- return {
546
- messageId: args.messageId,
547
- accountId: args.accountId,
548
- ...(args.kind ? { kind: args.kind } : {}),
549
- ...(typeof args.retryable === 'boolean' ? { retryable: args.retryable } : {}),
550
- retryCount: args.retryCount,
551
- error:
552
- (typeof args.lastError === 'string' && args.lastError) || OUTBOUND_TERMINAL_REASON.PUSH_RETRY,
553
- };
554
- }
555
-
556
247
  export function buildEnqueueFromReplyDebugInfo(args: {
557
248
  accountId: string;
558
249
  sessionKey: string;
@@ -586,18 +277,3 @@ export function buildEnqueueFromReplyDebugInfo(args: {
586
277
  },
587
278
  };
588
279
  }
589
-
590
- export function buildReplyMediaFallbackDebugInfo(args: {
591
- sessionKey: string;
592
- mediaUrl: string;
593
- replyToId: string;
594
- fallback: { text: string; reason: string };
595
- }) {
596
- return {
597
- sessionKey: args.sessionKey,
598
- mediaUrl: args.mediaUrl,
599
- replyToId: args.replyToId || undefined,
600
- fallbackText: args.fallback.text,
601
- reason: args.fallback.reason,
602
- };
603
- }
@@ -6,6 +6,7 @@ import type {
6
6
  ChannelMessageSendTextContext,
7
7
  } from 'openclaw/plugin-sdk/channel-message';
8
8
  import { defineChannelMessageAdapter } from 'openclaw/plugin-sdk/channel-message';
9
+ import type { OpenClawConfig } from 'openclaw/plugin-sdk/core';
9
10
 
10
11
  import {
11
12
  buildFileTransferOutboxEntry,
@@ -14,23 +15,25 @@ import {
14
15
  import type { BncrRoute, OutboxEntry } from '../../core/types.ts';
15
16
  import { buildBncrDurableQueuedResult } from './durable-queue-adapter.ts';
16
17
 
17
- export type BncrDurableMessageQueuedAdapterDeps<TConfig = unknown> = {
18
- enqueueText: (ctx: ChannelMessageSendTextContext<TConfig>) => Promise<OutboxEntry> | OutboxEntry;
18
+ export type BncrDurableMessageQueuedAdapterDeps = {
19
+ enqueueText: (
20
+ ctx: ChannelMessageSendTextContext<OpenClawConfig>,
21
+ ) => Promise<OutboxEntry> | OutboxEntry;
19
22
  enqueueMedia?: (
20
- ctx: ChannelMessageSendMediaContext<TConfig>,
23
+ ctx: ChannelMessageSendMediaContext<OpenClawConfig>,
21
24
  ) => Promise<OutboxEntry> | OutboxEntry;
22
25
  enqueuePayload?: (
23
- ctx: ChannelMessageSendPayloadContext<TConfig>,
26
+ ctx: ChannelMessageSendPayloadContext<OpenClawConfig>,
24
27
  ) => Promise<OutboxEntry> | OutboxEntry;
25
28
  now?: () => number;
26
29
  };
27
30
 
28
- export type BncrDurableMessageQueuedAdapterBuilderDeps<TConfig = unknown> = {
31
+ export type BncrDurableMessageQueuedAdapterBuilderDeps = {
29
32
  createMessageId: () => string;
30
33
  now: () => number;
31
34
  normalizeAccountId: (accountId?: string | null) => string;
32
35
  normalizeReplyToId: (value?: string | null) => string;
33
- resolveTarget: (ctx: ChannelMessageSendTextContext<TConfig>) => {
36
+ resolveTarget: (ctx: ChannelMessageSendTextContext<OpenClawConfig>) => {
34
37
  route: BncrRoute;
35
38
  sessionKey: string;
36
39
  accountId?: string | null;
@@ -41,9 +44,9 @@ export type BncrDurableMessageQueuedAdapterBuilderDeps<TConfig = unknown> = {
41
44
  // This adapter intentionally models only the OpenClaw -> bncr-plugin handoff.
42
45
  // Once a message is accepted into bncr's own outbox, OpenClaw should stop managing it;
43
46
  // client/platform ACK, retry, and deadLetter remain owned by the bncr service framework.
44
- export function createBncrDurableMessageQueuedAdapter<TConfig = unknown>(
45
- deps: BncrDurableMessageQueuedAdapterDeps<TConfig>,
46
- ): ChannelMessageAdapterShape<TConfig, ChannelMessageSendResult> {
47
+ export function createBncrDurableMessageQueuedAdapter(
48
+ deps: BncrDurableMessageQueuedAdapterDeps,
49
+ ): ChannelMessageAdapterShape<OpenClawConfig, ChannelMessageSendResult> {
47
50
  return defineChannelMessageAdapter({
48
51
  id: 'bncr-queued-outbox',
49
52
  receive: {
@@ -62,10 +65,10 @@ export function createBncrDurableMessageQueuedAdapter<TConfig = unknown>(
62
65
  });
63
66
  }
64
67
 
65
- export function createBncrDurableMessageQueuedAdapterFromBuilders<TConfig = unknown>(
66
- deps: BncrDurableMessageQueuedAdapterBuilderDeps<TConfig>,
67
- ): ChannelMessageAdapterShape<TConfig, ChannelMessageSendResult> {
68
- return createBncrDurableMessageQueuedAdapter<TConfig>({
68
+ export function createBncrDurableMessageQueuedAdapterFromBuilders(
69
+ deps: BncrDurableMessageQueuedAdapterBuilderDeps,
70
+ ): ChannelMessageAdapterShape<OpenClawConfig, ChannelMessageSendResult> {
71
+ return createBncrDurableMessageQueuedAdapter({
69
72
  now: deps.now,
70
73
  enqueueText: (ctx) => {
71
74
  const resolved = deps.resolveTarget(ctx);
@@ -74,7 +77,7 @@ export function createBncrDurableMessageQueuedAdapterFromBuilders<TConfig = unkn
74
77
  now: deps.now,
75
78
  normalizeAccountId: deps.normalizeAccountId,
76
79
  normalizeReplyToId: deps.normalizeReplyToId,
77
- accountId: resolved.accountId ?? ctx.accountId ?? undefined,
80
+ accountId: resolved.accountId ?? ctx.accountId ?? '',
78
81
  sessionKey: resolved.sessionKey,
79
82
  route: resolved.route,
80
83
  text: ctx.text,
@@ -89,7 +92,7 @@ export function createBncrDurableMessageQueuedAdapterFromBuilders<TConfig = unkn
89
92
  now: deps.now,
90
93
  normalizeAccountId: deps.normalizeAccountId,
91
94
  pushEvent: deps.filePushEvent ?? 'bncr.file.push',
92
- accountId: resolved.accountId ?? ctx.accountId ?? undefined,
95
+ accountId: resolved.accountId ?? ctx.accountId ?? '',
93
96
  sessionKey: resolved.sessionKey,
94
97
  route: resolved.route,
95
98
  mediaUrl: ctx.mediaUrl,
@@ -110,8 +113,9 @@ function toChannelMessageSendResult(
110
113
  ): ChannelMessageSendResult {
111
114
  if (!entry) throw new Error('bncr durable message adapter did not receive an outbox entry');
112
115
  const queued = buildBncrDurableQueuedResult({ entry, sentAt: now?.() });
116
+ const receipt: ChannelMessageSendResult['receipt'] = queued.receipt;
113
117
  return {
114
- receipt: queued.receipt as any,
118
+ receipt,
115
119
  messageId: queued.receipt.primaryPlatformMessageId,
116
120
  };
117
121
  }
@@ -65,6 +65,14 @@ export type BncrDurableQueuedResult = {
65
65
  }>;
66
66
  };
67
67
 
68
+ type OutboxPayload = OutboxEntry['payload'];
69
+ type OutboxMeta = NonNullable<OutboxPayload['_meta']>;
70
+ type OutboxMessagePayload = { type?: unknown };
71
+
72
+ function getOutboxPayload(entry: OutboxEntry): OutboxPayload {
73
+ return entry.payload;
74
+ }
75
+
68
76
  export function buildBncrDurableQueuedResult(args: {
69
77
  entry: OutboxEntry;
70
78
  index?: number;
@@ -131,24 +139,29 @@ export function buildBncrDurableQueuedResult(args: {
131
139
  }
132
140
 
133
141
  function extractPayloadType(entry: OutboxEntry): string | undefined {
134
- const payload = entry.payload as any;
142
+ const payload = getOutboxPayload(entry);
135
143
  return typeof payload?.type === 'string' ? payload.type : undefined;
136
144
  }
137
145
 
138
146
  function extractReplyToId(entry: OutboxEntry): string | undefined {
139
- const payload = entry.payload as any;
140
- const metaReply = payload?._meta?.replyToId;
147
+ const payload = getOutboxPayload(entry);
148
+ const metaReply = (payload?._meta as OutboxMeta | undefined)?.replyToId;
141
149
  const replyToId = payload?.replyToId ?? metaReply;
142
150
  return typeof replyToId === 'string' ? replyToId : undefined;
143
151
  }
144
152
 
145
153
  function inferReceiptKind(entry: OutboxEntry): 'text' | 'media' | 'voice' | 'unknown' {
146
- const payload = entry.payload as any;
147
- if (payload?._meta?.kind === 'file-transfer') {
148
- if (payload?._meta?.asVoice === true || payload?._meta?.audioAsVoice === true) return 'voice';
154
+ const payload = getOutboxPayload(entry);
155
+ const meta = payload?._meta as OutboxMeta | undefined;
156
+ const message =
157
+ payload.message && typeof payload.message === 'object'
158
+ ? (payload.message as OutboxMessagePayload)
159
+ : null;
160
+ if (meta?.kind === 'file-transfer') {
161
+ if (meta.asVoice === true || meta.audioAsVoice === true) return 'voice';
149
162
  return 'media';
150
163
  }
151
- if (payload?.message?.type === 'text') return 'text';
164
+ if (message?.type === 'text') return 'text';
152
165
  return 'unknown';
153
166
  }
154
167
 
@@ -1,5 +1,7 @@
1
1
  import { normalizeOutboundReplyToId } from './reply-target-policy.ts';
2
2
 
3
+ type BncrOutboundMessageType = 'text' | 'image' | 'video' | 'voice' | 'audio' | 'file';
4
+
3
5
  function asString(v: unknown, fallback = ''): string {
4
6
  if (typeof v === 'string') return v;
5
7
  if (v == null) return fallback;
@@ -11,34 +13,43 @@ function isAudioMimeType(mimeType?: string): boolean {
11
13
  return mt.startsWith('audio/');
12
14
  }
13
15
 
16
+ function isBncrOutboundMessageType(value: string): value is BncrOutboundMessageType {
17
+ return (
18
+ value === 'text' ||
19
+ value === 'image' ||
20
+ value === 'video' ||
21
+ value === 'voice' ||
22
+ value === 'audio' ||
23
+ value === 'file'
24
+ );
25
+ }
26
+
27
+ function isMimeMajorOutboundMessageType(
28
+ value: string,
29
+ ): value is Exclude<BncrOutboundMessageType, 'voice' | 'file'> {
30
+ return value === 'text' || value === 'image' || value === 'video' || value === 'audio';
31
+ }
32
+
14
33
  export function resolveBncrOutboundMessageType(params: {
15
34
  mimeType?: string;
16
35
  fileName?: string;
17
36
  hintedType?: string;
18
37
  hasPayload?: boolean;
19
- }): 'text' | 'image' | 'video' | 'voice' | 'audio' | 'file' {
38
+ }): BncrOutboundMessageType {
20
39
  const hinted = asString(params.hintedType || '').toLowerCase();
21
40
  const hasPayload = !!params.hasPayload;
22
41
  const mt = asString(params.mimeType || '').toLowerCase();
23
42
  const major = mt.split('/')[0] || '';
24
- const isStandard =
25
- hinted === 'text' ||
26
- hinted === 'image' ||
27
- hinted === 'video' ||
28
- hinted === 'voice' ||
29
- hinted === 'audio' ||
30
- hinted === 'file';
43
+ const isStandard = isBncrOutboundMessageType(hinted);
31
44
 
32
45
  if (hasPayload && major === 'text' && (hinted === 'text' || !isStandard)) return 'file';
33
46
  if (hinted === 'voice') {
34
47
  if (isAudioMimeType(mt)) return 'voice';
35
- if (major === 'text' || major === 'image' || major === 'video' || major === 'audio')
36
- return major as any;
48
+ if (isMimeMajorOutboundMessageType(major)) return major;
37
49
  return 'file';
38
50
  }
39
- if (isStandard) return hinted as any;
40
- if (major === 'text' || major === 'image' || major === 'video' || major === 'audio')
41
- return major as any;
51
+ if (isStandard) return hinted;
52
+ if (isMimeMajorOutboundMessageType(major)) return major;
42
53
  return 'file';
43
54
  }
44
55