@xmoxmo/bncr 0.3.6 → 0.3.8

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 (165) hide show
  1. package/README.md +5 -0
  2. package/dist/index.js +28 -5
  3. package/index.ts +55 -721
  4. package/openclaw.plugin.json +1 -0
  5. package/package.json +8 -4
  6. package/scripts/check-pack.mjs +93 -18
  7. package/scripts/check-register-drift.mjs +35 -13
  8. package/scripts/selfcheck.mjs +80 -11
  9. package/src/bootstrap/channel-plugin-runtime.ts +81 -0
  10. package/src/bootstrap/cli.ts +97 -0
  11. package/src/bootstrap/register-runtime-gateway.ts +129 -0
  12. package/src/bootstrap/register-runtime-helpers.ts +140 -0
  13. package/src/bootstrap/register-runtime-singleton.ts +137 -0
  14. package/src/bootstrap/register-runtime.ts +201 -0
  15. package/src/bootstrap/runtime-discovery.ts +187 -0
  16. package/src/bootstrap/runtime-loader.ts +54 -0
  17. package/src/channel.ts +1590 -4967
  18. package/src/core/accounts.ts +23 -4
  19. package/src/core/dead-letter-diagnostics.ts +37 -5
  20. package/src/core/diagnostics.ts +31 -15
  21. package/src/core/downlink-health.ts +3 -11
  22. package/src/core/extended-diagnostics.ts +78 -36
  23. package/src/core/file-transfer-payloads.ts +1 -1
  24. package/src/core/logging.ts +1 -0
  25. package/src/core/outbox-enqueue.ts +13 -2
  26. package/src/core/outbox-entry-builders.ts +2 -0
  27. package/src/core/outbox-summary.ts +75 -3
  28. package/src/core/permissions.ts +15 -2
  29. package/src/core/persisted-outbox-entry.ts +21 -6
  30. package/src/core/policy.ts +45 -4
  31. package/src/core/probe.ts +3 -15
  32. package/src/core/register-trace.ts +3 -3
  33. package/src/core/status.ts +43 -4
  34. package/src/core/targets.ts +216 -205
  35. package/src/core/types.ts +221 -0
  36. package/src/core/value-sanitize.ts +29 -0
  37. package/src/messaging/inbound/commands.ts +147 -172
  38. package/src/messaging/inbound/context-facts.ts +4 -2
  39. package/src/messaging/inbound/contracts.ts +70 -0
  40. package/src/messaging/inbound/dispatch-prep.ts +303 -0
  41. package/src/messaging/inbound/dispatch.ts +49 -462
  42. package/src/messaging/inbound/gate.ts +18 -5
  43. package/src/messaging/inbound/last-route.ts +10 -4
  44. package/src/messaging/inbound/media-url-download.ts +109 -0
  45. package/src/messaging/inbound/native-command-runtime.ts +225 -0
  46. package/src/messaging/inbound/parse.ts +2 -1
  47. package/src/messaging/inbound/remote-media.ts +49 -0
  48. package/src/messaging/inbound/reply-config.ts +16 -4
  49. package/src/messaging/inbound/reply-dispatch.ts +162 -0
  50. package/src/messaging/inbound/runtime-compat.ts +31 -10
  51. package/src/messaging/inbound/session-label.ts +15 -7
  52. package/src/messaging/inbound/turn-context.ts +131 -0
  53. package/src/messaging/outbound/actions.ts +24 -10
  54. package/src/messaging/outbound/diagnostics-debug-builders.ts +365 -0
  55. package/src/messaging/outbound/diagnostics.ts +31 -355
  56. package/src/messaging/outbound/durable-message-adapter.ts +20 -16
  57. package/src/messaging/outbound/durable-queue-adapter.ts +20 -7
  58. package/src/messaging/outbound/media.ts +24 -13
  59. package/src/messaging/outbound/reply-enqueue-media.ts +181 -0
  60. package/src/messaging/outbound/reply-enqueue.ts +46 -155
  61. package/src/messaging/outbound/send-params.ts +3 -0
  62. package/src/messaging/outbound/send.ts +19 -10
  63. package/src/messaging/outbound/session-route.ts +18 -3
  64. package/src/openclaw/channel-runtime-contracts.ts +76 -0
  65. package/src/openclaw/config-runtime.ts +13 -7
  66. package/src/openclaw/inbound-session-runtime.ts +7 -3
  67. package/src/openclaw/ingress-runtime.ts +17 -27
  68. package/src/openclaw/reply-runtime.ts +54 -59
  69. package/src/openclaw/routing-runtime.ts +35 -18
  70. package/src/openclaw/runtime-surface.ts +156 -12
  71. package/src/openclaw/sdk-helpers.ts +8 -1
  72. package/src/openclaw/session-route-runtime.ts +12 -12
  73. package/src/plugin/ack-outbox-runtime-group.ts +264 -0
  74. package/src/plugin/bridge-ack-facade.ts +137 -0
  75. package/src/plugin/bridge-connection-facade.ts +111 -0
  76. package/src/plugin/bridge-diagnostics-facade.ts +23 -0
  77. package/src/plugin/bridge-drain-facade.ts +98 -0
  78. package/src/plugin/bridge-extended-diagnostics-facade.ts +149 -0
  79. package/src/plugin/bridge-file-transfer-push-facade.ts +140 -0
  80. package/src/plugin/bridge-lifecycle.ts +156 -0
  81. package/src/plugin/bridge-media-facade.ts +241 -0
  82. package/src/plugin/bridge-outbox-facade.ts +182 -0
  83. package/src/plugin/bridge-runtime-helpers.ts +266 -0
  84. package/src/plugin/bridge-runtime-snapshots.ts +104 -0
  85. package/src/plugin/bridge-runtime-surface-facade.ts +8 -0
  86. package/src/plugin/bridge-status-facade.ts +76 -0
  87. package/src/plugin/bridge-status-worker-facade.ts +72 -0
  88. package/src/plugin/bridge-support-runtime.ts +137 -0
  89. package/src/plugin/bridge-surface-handlers-group.ts +242 -0
  90. package/src/plugin/bridge-surface-helpers.ts +28 -0
  91. package/src/plugin/capabilities.ts +1 -3
  92. package/src/plugin/channel-components.ts +289 -0
  93. package/src/plugin/channel-inbound-helpers.ts +149 -0
  94. package/src/plugin/channel-plugin-bridge-group.ts +129 -0
  95. package/src/plugin/channel-plugin-surface-group.ts +202 -0
  96. package/src/plugin/channel-runtime-builders-delivery.ts +513 -0
  97. package/src/plugin/channel-runtime-builders-status.ts +331 -0
  98. package/src/plugin/channel-runtime-builders.ts +25 -0
  99. package/src/plugin/channel-runtime-constants.ts +40 -0
  100. package/src/plugin/channel-runtime-types.ts +146 -0
  101. package/src/plugin/channel-send-runtime-group.ts +37 -0
  102. package/src/plugin/channel-send.ts +226 -0
  103. package/src/plugin/channel-utils.ts +102 -0
  104. package/src/plugin/config.ts +24 -3
  105. package/src/plugin/connection-handlers-helpers.ts +254 -0
  106. package/src/plugin/connection-handlers.ts +440 -0
  107. package/src/plugin/connection-state-helpers.ts +159 -0
  108. package/src/plugin/connection-state-runtime-group.ts +51 -0
  109. package/src/plugin/connection-state.ts +527 -0
  110. package/src/plugin/diagnostics-handlers.ts +211 -0
  111. package/src/plugin/error-message.ts +15 -0
  112. package/src/plugin/file-ack-runtime.ts +284 -0
  113. package/src/plugin/file-inbound-abort.ts +112 -0
  114. package/src/plugin/file-inbound-chunk.ts +146 -0
  115. package/src/plugin/file-inbound-complete.ts +153 -0
  116. package/src/plugin/file-inbound-handlers.ts +19 -0
  117. package/src/plugin/file-inbound-init.ts +122 -0
  118. package/src/plugin/file-inbound-runtime.ts +51 -0
  119. package/src/plugin/file-inbound-state.ts +62 -0
  120. package/src/plugin/file-transfer-logs.ts +227 -0
  121. package/src/plugin/file-transfer-orchestrator-chunk.ts +135 -0
  122. package/src/plugin/file-transfer-orchestrator.ts +304 -0
  123. package/src/plugin/file-transfer-runtime-group.ts +102 -0
  124. package/src/plugin/file-transfer-send.ts +89 -0
  125. package/src/plugin/file-transfer-setup.ts +206 -0
  126. package/src/plugin/gateway-event-context.ts +41 -0
  127. package/src/plugin/gateway-runtime.ts +17 -4
  128. package/src/plugin/inbound-acceptance.ts +107 -0
  129. package/src/plugin/inbound-handlers.ts +248 -0
  130. package/src/plugin/inbound-surface-handlers-group.ts +152 -0
  131. package/src/plugin/media-dedupe-runtime.ts +90 -0
  132. package/src/plugin/media-orchestrators-runtime-group.ts +316 -0
  133. package/src/plugin/message-ack-runtime.ts +284 -0
  134. package/src/plugin/message-send.ts +16 -6
  135. package/src/plugin/messaging.ts +98 -36
  136. package/src/plugin/outbound.ts +50 -8
  137. package/src/plugin/outbox-ack-logs.ts +136 -0
  138. package/src/plugin/outbox-ack-outcome.ts +128 -0
  139. package/src/plugin/outbox-drain-ack.ts +145 -0
  140. package/src/plugin/outbox-drain-failure.ts +84 -0
  141. package/src/plugin/outbox-drain-loop.ts +554 -0
  142. package/src/plugin/outbox-drain-post-push.ts +159 -0
  143. package/src/plugin/outbox-drain-runtime.ts +141 -0
  144. package/src/plugin/outbox-drain-schedule.ts +116 -0
  145. package/src/plugin/outbox-file-push-flow.ts +69 -0
  146. package/src/plugin/outbox-push-route-runtime-group.ts +81 -0
  147. package/src/plugin/outbox-push.ts +267 -0
  148. package/src/plugin/outbox-route.ts +181 -0
  149. package/src/plugin/outbox-text-push-flow.ts +90 -0
  150. package/src/plugin/runtime-diagnostics-assembler.ts +183 -0
  151. package/src/plugin/runtime-diagnostics-helpers.ts +302 -0
  152. package/src/plugin/runtime-diagnostics-payload-builders.ts +171 -0
  153. package/src/plugin/runtime-diagnostics-snapshot.ts +31 -0
  154. package/src/plugin/setup.ts +33 -6
  155. package/src/plugin/state-store.ts +249 -0
  156. package/src/plugin/state-transient-runtime-group.ts +105 -0
  157. package/src/plugin/status-runtime.ts +251 -0
  158. package/src/plugin/status.ts +33 -7
  159. package/src/plugin/target-runtime.ts +141 -0
  160. package/src/plugin/target-status-runtime-group.ts +130 -0
  161. package/src/plugin/transient-state-runtime.ts +82 -0
  162. package/src/runtime/outbound-ack-timeout.ts +5 -3
  163. package/src/runtime/outbound-flags.ts +24 -8
  164. package/src/runtime/status-snapshots.ts +36 -7
  165. package/src/runtime/status-worker.ts +34 -4
@@ -1,10 +1,17 @@
1
1
  import { emitBncrLogLine } from '../../core/logging.ts';
2
+ import type { OpenClawChannelRuntimeContext } from '../../openclaw/channel-runtime-contracts.ts';
2
3
  import {
3
4
  recordBncrSessionMetaFromInbound,
4
5
  updateBncrSessionStoreEntry,
5
6
  } from '../../openclaw/inbound-session-runtime.ts';
6
7
 
7
- type RecordInboundSessionFn = (args: any) => Promise<unknown> | unknown;
8
+ type RecordInboundSessionFn = (args: {
9
+ storePath?: string;
10
+ sessionKey?: string;
11
+ [key: string]: unknown;
12
+ }) => Promise<unknown> | unknown;
13
+
14
+ type SessionStoreEntryLike = { label?: string | null; [key: string]: unknown };
8
15
 
9
16
  export function buildBncrInboundSessionIdentityPatch(args: {
10
17
  channelId: string;
@@ -60,7 +67,7 @@ export async function correctBncrInboundSessionLabel(args: {
60
67
  await updateBncrSessionStoreEntry({
61
68
  storePath,
62
69
  sessionKey,
63
- update: (entry: any) => {
70
+ update: (entry: SessionStoreEntryLike) => {
64
71
  if (entry?.label === expectedLabel) return null;
65
72
  return { label: expectedLabel };
66
73
  },
@@ -73,7 +80,7 @@ export async function correctBncrInboundSessionLabel(args: {
73
80
  export async function recordAndPatchBncrInboundSessionEntry(args: {
74
81
  storePath: string;
75
82
  sessionKey: string;
76
- ctx?: Record<string, unknown>;
83
+ ctx?: OpenClawChannelRuntimeContext;
77
84
  patch: Record<string, unknown>;
78
85
  }) {
79
86
  const storePath = normalizeNonEmptyString(args.storePath);
@@ -85,7 +92,7 @@ export async function recordAndPatchBncrInboundSessionEntry(args: {
85
92
  await recordBncrSessionMetaFromInbound({
86
93
  storePath,
87
94
  sessionKey,
88
- ctx: args.ctx as any,
95
+ ctx: args.ctx,
89
96
  createIfMissing: true,
90
97
  });
91
98
  }
@@ -103,11 +110,12 @@ export function wrapBncrInboundRecordSessionLabelCorrection(args: {
103
110
  recordInboundSession: RecordInboundSessionFn;
104
111
  expectedLabel: string;
105
112
  }): RecordInboundSessionFn {
106
- return async (recordArgs: any) => {
113
+ return async (recordArgs) => {
107
114
  const result = await args.recordInboundSession(recordArgs);
115
+ if (!recordArgs?.storePath || !recordArgs?.sessionKey) return result;
108
116
  await correctBncrInboundSessionLabel({
109
- storePath: recordArgs?.storePath,
110
- sessionKey: recordArgs?.sessionKey,
117
+ storePath: recordArgs.storePath,
118
+ sessionKey: recordArgs.sessionKey,
111
119
  expectedLabel: args.expectedLabel,
112
120
  });
113
121
  return result;
@@ -0,0 +1,131 @@
1
+ import {
2
+ buildBncrPromptVisibleContextFacts,
3
+ buildBncrStructuredContextFactsFromInboundParts,
4
+ } from './context-facts.ts';
5
+ import type { BncrInboundApi, BncrInboundContextPayload } from './contracts.ts';
6
+ import type {
7
+ BncrInboundConversationResolution,
8
+ BncrPreparedInboundSessionContext,
9
+ ParsedInbound,
10
+ } from './dispatch-prep.ts';
11
+ import { resolveBncrChannelInboundRuntime } from './runtime-compat.ts';
12
+
13
+ export function buildBncrInboundTurnContext(args: {
14
+ api: BncrInboundApi;
15
+ channelId: string;
16
+ parsed: ParsedInbound;
17
+ msgId?: string | null;
18
+ mimeType?: string;
19
+ mediaPath?: string;
20
+ peer: ParsedInbound['peer'];
21
+ senderIdForContext: string;
22
+ senderDisplayName: string;
23
+ resolution: BncrInboundConversationResolution;
24
+ prepared: BncrPreparedInboundSessionContext;
25
+ }): BncrInboundContextPayload | Promise<BncrInboundContextPayload> {
26
+ const {
27
+ api,
28
+ channelId,
29
+ parsed,
30
+ msgId,
31
+ mimeType,
32
+ mediaPath,
33
+ peer,
34
+ senderIdForContext,
35
+ senderDisplayName,
36
+ resolution,
37
+ prepared,
38
+ } = args;
39
+ const structuredContextFacts = buildBncrStructuredContextFactsFromInboundParts({
40
+ channelId,
41
+ parsed,
42
+ resolution,
43
+ prepared: {
44
+ rawBody: prepared.rawBody,
45
+ body: prepared.body,
46
+ mediaPath,
47
+ mediaContentType: mimeType,
48
+ },
49
+ senderIdForContext,
50
+ senderDisplayName,
51
+ });
52
+ const promptVisibleContextFacts = buildBncrPromptVisibleContextFacts(structuredContextFacts);
53
+ const supplementalUntrustedContext = Object.keys(promptVisibleContextFacts).length
54
+ ? [
55
+ {
56
+ label: 'Bncr inbound context',
57
+ source: channelId,
58
+ type: 'bncr.inbound_context',
59
+ payload: promptVisibleContextFacts,
60
+ },
61
+ ]
62
+ : [];
63
+
64
+ return resolveBncrChannelInboundRuntime(api).buildContext({
65
+ channel: channelId,
66
+ provider: channelId,
67
+ surface: channelId,
68
+ accountId: resolution.accountId,
69
+ messageId: msgId,
70
+ timestamp: Date.now(),
71
+ from: senderIdForContext,
72
+ sender: {
73
+ id: senderIdForContext,
74
+ name: senderDisplayName,
75
+ username: senderDisplayName,
76
+ },
77
+ conversation: {
78
+ kind: resolution.chatType,
79
+ id: peer.id,
80
+ label: resolution.canonicalTo,
81
+ routePeer: {
82
+ kind: peer.kind,
83
+ id: peer.id,
84
+ },
85
+ },
86
+ route: {
87
+ agentId: resolution.resolvedRoute.agentId,
88
+ accountId: resolution.accountId,
89
+ routeSessionKey: resolution.resolvedRoute.sessionKey,
90
+ dispatchSessionKey: resolution.dispatchSessionKey,
91
+ mainSessionKey: resolution.resolvedRoute.mainSessionKey,
92
+ },
93
+ reply: {
94
+ to: resolution.canonicalTo,
95
+ originatingTo: resolution.originatingTo,
96
+ },
97
+ message: {
98
+ inboundEventKind: 'user_request',
99
+ body: prepared.body,
100
+ rawBody: prepared.rawBody,
101
+ bodyForAgent: prepared.rawBody,
102
+ commandBody: prepared.rawBody,
103
+ envelopeFrom: resolution.originatingTo,
104
+ senderLabel: senderDisplayName,
105
+ },
106
+ media: mediaPath
107
+ ? [
108
+ {
109
+ path: mediaPath,
110
+ contentType: mimeType,
111
+ kind: mimeType?.startsWith('image/')
112
+ ? 'image'
113
+ : mimeType?.startsWith('video/')
114
+ ? 'video'
115
+ : mimeType?.startsWith('audio/')
116
+ ? 'audio'
117
+ : 'document',
118
+ messageId: msgId ?? undefined,
119
+ },
120
+ ]
121
+ : [],
122
+ supplemental: {
123
+ untrustedContext: supplementalUntrustedContext,
124
+ },
125
+ extra: {
126
+ OriginatingChannel: channelId,
127
+ BncrStructuredContextFacts: structuredContextFacts,
128
+ StructuredContextFacts: structuredContextFacts,
129
+ },
130
+ });
131
+ }
@@ -1,15 +1,29 @@
1
+ type BncrReplyActionSendTextParams = {
2
+ accountId: string;
3
+ to: string;
4
+ text: string;
5
+ replyToMessageId?: string;
6
+ };
7
+
8
+ type BncrReplyActionResult = {
9
+ channel: string;
10
+ messageId: string;
11
+ chatId: string;
12
+ };
13
+
14
+ type BncrUnsupportedActionResult = {
15
+ ok: false;
16
+ unsupported: true;
17
+ reason: string;
18
+ };
19
+
1
20
  export async function sendBncrReplyAction(params: {
2
21
  accountId: string;
3
22
  to: string;
4
23
  text: string;
5
24
  replyToMessageId?: string;
6
- sendText: (params: {
7
- accountId: string;
8
- to: string;
9
- text: string;
10
- replyToMessageId?: string;
11
- }) => Promise<any>;
12
- }) {
25
+ sendText: (params: BncrReplyActionSendTextParams) => Promise<BncrReplyActionResult>;
26
+ }): Promise<BncrReplyActionResult> {
13
27
  return params.sendText({
14
28
  accountId: params.accountId,
15
29
  to: params.to,
@@ -21,7 +35,7 @@ export async function sendBncrReplyAction(params: {
21
35
  export async function deleteBncrMessageAction(_params: {
22
36
  accountId: string;
23
37
  targetMessageId: string;
24
- }) {
38
+ }): Promise<BncrUnsupportedActionResult> {
25
39
  return { ok: false, unsupported: true, reason: 'delete not implemented yet' };
26
40
  }
27
41
 
@@ -29,7 +43,7 @@ export async function reactBncrMessageAction(_params: {
29
43
  accountId: string;
30
44
  targetMessageId: string;
31
45
  emoji: string;
32
- }) {
46
+ }): Promise<BncrUnsupportedActionResult> {
33
47
  return { ok: false, unsupported: true, reason: 'react not implemented yet' };
34
48
  }
35
49
 
@@ -37,6 +51,6 @@ export async function editBncrMessageAction(_params: {
37
51
  accountId: string;
38
52
  targetMessageId: string;
39
53
  text: string;
40
- }) {
54
+ }): Promise<BncrUnsupportedActionResult> {
41
55
  return { ok: false, unsupported: true, reason: 'edit not implemented yet' };
42
56
  }
@@ -0,0 +1,365 @@
1
+ import type { BncrConnection } from '../../core/types.ts';
2
+ import { OUTBOUND_TERMINAL_REASON, type OutboundScheduleSource } from './reasons.ts';
3
+ import type { RetryRerouteDecision } from './retry-policy.ts';
4
+
5
+ type BncrConnectionDebugView = BncrConnection & {
6
+ outboundReadyUntil?: number;
7
+ preferredForOutboundUntil?: number;
8
+ lastAckOkAt?: number;
9
+ lastPushTimeoutAt?: number;
10
+ pushFailureScore?: number;
11
+ };
12
+
13
+ function asConnectionDebugView(connection: BncrConnection): BncrConnectionDebugView {
14
+ return connection as BncrConnectionDebugView;
15
+ }
16
+
17
+ export function buildOutboxScheduleDebugInfo(args: {
18
+ bridgeId: string;
19
+ accountId?: string | null;
20
+ localNextDelay?: number | null;
21
+ globalNextDelay?: number | null;
22
+ wait?: number | null;
23
+ source: OutboundScheduleSource;
24
+ messageId?: string;
25
+ }) {
26
+ return {
27
+ bridge: args.bridgeId,
28
+ ...(args.accountId ? { accountId: args.accountId } : {}),
29
+ ...(args.messageId ? { messageId: args.messageId } : {}),
30
+ source: args.source,
31
+ ...(typeof args.wait === 'number' ? { wait: args.wait } : {}),
32
+ ...(typeof args.localNextDelay === 'number' ? { localNextDelay: args.localNextDelay } : {}),
33
+ ...(typeof args.globalNextDelay === 'number' ? { globalNextDelay: args.globalNextDelay } : {}),
34
+ };
35
+ }
36
+
37
+ export function buildOutboxPushSkipDebugInfo(args: {
38
+ messageId: string;
39
+ accountId: string;
40
+ reason: string;
41
+ recentInboundReachable?: boolean;
42
+ kind?: string;
43
+ routeReason?: string;
44
+ connIds?: Iterable<string>;
45
+ ownerConnId?: string;
46
+ ownerClientId?: string;
47
+ activeConnectionCount?: number;
48
+ connections?: Iterable<BncrConnection>;
49
+ }) {
50
+ return {
51
+ messageId: args.messageId,
52
+ accountId: args.accountId,
53
+ ...(args.kind ? { kind: args.kind } : {}),
54
+ reason: args.reason,
55
+ ...(args.routeReason ? { routeReason: args.routeReason } : {}),
56
+ ...(args.connIds ? { connIds: Array.from(args.connIds) } : {}),
57
+ ...(args.ownerConnId ? { ownerConnId: args.ownerConnId } : {}),
58
+ ...(args.ownerClientId ? { ownerClientId: args.ownerClientId } : {}),
59
+ ...(typeof args.activeConnectionCount === 'number'
60
+ ? { activeConnectionCount: args.activeConnectionCount }
61
+ : {}),
62
+ ...(args.connections
63
+ ? {
64
+ connections: Array.from(args.connections)
65
+ .filter((c) => c.accountId === args.accountId)
66
+ .slice(0, 8)
67
+ .map((c) => {
68
+ const view = asConnectionDebugView(c);
69
+ return {
70
+ connId: c.connId,
71
+ clientId: c.clientId,
72
+ lastSeenAt: c.lastSeenAt,
73
+ outboundReadyUntil: view.outboundReadyUntil,
74
+ preferredForOutboundUntil: view.preferredForOutboundUntil,
75
+ inboundOnly: view.inboundOnly,
76
+ lastAckOkAt: view.lastAckOkAt,
77
+ lastPushTimeoutAt: view.lastPushTimeoutAt,
78
+ pushFailureScore: view.pushFailureScore,
79
+ };
80
+ }),
81
+ }
82
+ : {}),
83
+ ...(typeof args.recentInboundReachable === 'boolean'
84
+ ? { recentInboundReachable: args.recentInboundReachable }
85
+ : {}),
86
+ };
87
+ }
88
+
89
+ export function buildOutboxRouteSelectDebugInfo(args: {
90
+ messageId: string;
91
+ accountId: string;
92
+ routeReason: string;
93
+ connIds: Iterable<string>;
94
+ ownerConnId?: string;
95
+ ownerClientId?: string;
96
+ recentInboundReachable: boolean;
97
+ event: string;
98
+ kind?: string;
99
+ }) {
100
+ return {
101
+ messageId: args.messageId,
102
+ accountId: args.accountId,
103
+ ...(args.kind ? { kind: args.kind } : {}),
104
+ routeReason: args.routeReason,
105
+ connIds: Array.from(args.connIds),
106
+ ownerConnId: args.ownerConnId || '',
107
+ ownerClientId: args.ownerClientId || '',
108
+ recentInboundReachable: args.recentInboundReachable,
109
+ event: args.event,
110
+ };
111
+ }
112
+
113
+ export function buildOutboxPushOkDebugInfo(args: {
114
+ messageId: string;
115
+ accountId: string;
116
+ connIds: Iterable<string>;
117
+ ownerConnId?: string;
118
+ ownerClientId?: string;
119
+ recentInboundReachable: boolean;
120
+ event: string;
121
+ kind?: string;
122
+ }) {
123
+ return {
124
+ messageId: args.messageId,
125
+ accountId: args.accountId,
126
+ ...(args.kind ? { kind: args.kind } : {}),
127
+ connIds: Array.from(args.connIds),
128
+ ownerConnId: args.ownerConnId || '',
129
+ ownerClientId: args.ownerClientId || '',
130
+ recentInboundReachable: args.recentInboundReachable,
131
+ event: args.event,
132
+ };
133
+ }
134
+
135
+ export function buildFlushDebugInfo(args: {
136
+ bridgeId: string;
137
+ accountId: string | null;
138
+ targetAccounts: string[];
139
+ outboxSize: number;
140
+ trigger: string;
141
+ reason?: string;
142
+ }) {
143
+ return {
144
+ bridge: args.bridgeId,
145
+ accountId: args.accountId,
146
+ targetAccounts: [...args.targetAccounts],
147
+ outboxSize: args.outboxSize,
148
+ trigger: args.trigger,
149
+ reason: args.reason,
150
+ };
151
+ }
152
+
153
+ export function buildOutboxDrainSkipDebugInfo(args: {
154
+ bridgeId: string;
155
+ accountId: string;
156
+ reason: string;
157
+ outboxSize: number;
158
+ trigger: string;
159
+ }) {
160
+ return {
161
+ bridge: args.bridgeId,
162
+ accountId: args.accountId,
163
+ reason: args.reason,
164
+ outboxSize: args.outboxSize,
165
+ trigger: args.trigger,
166
+ };
167
+ }
168
+
169
+ export function buildOutboxDrainStuckDebugInfo(args: {
170
+ bridgeId: string;
171
+ accountId: string;
172
+ reason: string;
173
+ trigger: string;
174
+ outboxSize: number;
175
+ pending: number;
176
+ runningMs: number;
177
+ runningSince?: number | null;
178
+ hasGatewayContext: boolean;
179
+ activeConnectionCount: number;
180
+ messageAckWaiters: number;
181
+ fileAckWaiters: number;
182
+ pendingEntries?: Iterable<{
183
+ messageId?: string;
184
+ retryCount?: number;
185
+ nextAttemptAt?: number;
186
+ lastAttemptAt?: number;
187
+ lastError?: string;
188
+ lastPushAt?: number;
189
+ lastPushConnId?: string;
190
+ routeAttemptConnIds?: string[];
191
+ }>;
192
+ connections?: Iterable<BncrConnection>;
193
+ }) {
194
+ return {
195
+ bridge: args.bridgeId,
196
+ accountId: args.accountId,
197
+ reason: args.reason,
198
+ trigger: args.trigger,
199
+ outboxSize: args.outboxSize,
200
+ pending: args.pending,
201
+ runningMs: args.runningMs,
202
+ runningSince: args.runningSince ?? null,
203
+ hasGatewayContext: args.hasGatewayContext,
204
+ activeConnectionCount: args.activeConnectionCount,
205
+ waiters: {
206
+ messageAck: args.messageAckWaiters,
207
+ fileAck: args.fileAckWaiters,
208
+ },
209
+ ...(args.pendingEntries
210
+ ? {
211
+ pendingEntries: Array.from(args.pendingEntries)
212
+ .slice(0, 8)
213
+ .map((entry) => ({
214
+ messageId: entry.messageId || '',
215
+ retryCount: entry.retryCount,
216
+ nextAttemptAt: entry.nextAttemptAt,
217
+ lastAttemptAt: entry.lastAttemptAt,
218
+ lastError: entry.lastError,
219
+ lastPushAt: entry.lastPushAt,
220
+ lastPushConnId: entry.lastPushConnId,
221
+ routeAttemptConnIds: entry.routeAttemptConnIds,
222
+ })),
223
+ }
224
+ : {}),
225
+ ...(args.connections
226
+ ? {
227
+ connections: Array.from(args.connections)
228
+ .filter((c) => c.accountId === args.accountId)
229
+ .slice(0, 8)
230
+ .map((c) => {
231
+ const view = asConnectionDebugView(c);
232
+ return {
233
+ connId: c.connId,
234
+ clientId: c.clientId,
235
+ connectedAt: c.connectedAt,
236
+ lastSeenAt: c.lastSeenAt,
237
+ outboundReadyUntil: view.outboundReadyUntil,
238
+ preferredForOutboundUntil: view.preferredForOutboundUntil,
239
+ inboundOnly: view.inboundOnly,
240
+ lastAckOkAt: view.lastAckOkAt,
241
+ lastPushTimeoutAt: view.lastPushTimeoutAt,
242
+ pushFailureScore: view.pushFailureScore,
243
+ };
244
+ }),
245
+ }
246
+ : {}),
247
+ };
248
+ }
249
+
250
+ export function buildOutboxAckDebugInfo(args: {
251
+ messageId: string;
252
+ accountId: string;
253
+ requireAck: boolean;
254
+ ackResult: 'acked' | 'timeout';
255
+ onlineNow: boolean;
256
+ recentInboundReachable: boolean;
257
+ connIds?: Iterable<string>;
258
+ ownerConnId?: string;
259
+ ownerClientId?: string;
260
+ sessionKey?: string;
261
+ to?: string;
262
+ ackStage?: string;
263
+ ackOutcome?: string;
264
+ reason?: string;
265
+ kind?: string;
266
+ event?: string;
267
+ ackTimeoutMs?: number;
268
+ adaptiveAckTimeoutEnabled?: boolean;
269
+ }) {
270
+ return {
271
+ messageId: args.messageId,
272
+ accountId: args.accountId,
273
+ ...(args.sessionKey ? { sessionKey: args.sessionKey } : {}),
274
+ ...(args.to ? { to: args.to } : {}),
275
+ ...(args.kind ? { kind: args.kind } : {}),
276
+ requireAck: args.requireAck,
277
+ ackResult: args.ackResult,
278
+ ackStage: args.ackStage || 'message',
279
+ ackOutcome: args.ackOutcome || args.ackResult,
280
+ ...(args.reason ? { reason: args.reason } : {}),
281
+ ...(typeof args.ackTimeoutMs === 'number' ? { ackTimeoutMs: args.ackTimeoutMs } : {}),
282
+ ...(typeof args.adaptiveAckTimeoutEnabled === 'boolean'
283
+ ? { adaptiveAckTimeoutEnabled: args.adaptiveAckTimeoutEnabled }
284
+ : {}),
285
+ onlineNow: args.onlineNow,
286
+ recentInboundReachable: args.recentInboundReachable,
287
+ ...(args.connIds ? { connIds: Array.from(args.connIds) } : {}),
288
+ ...(args.ownerConnId ? { ownerConnId: args.ownerConnId } : {}),
289
+ ...(args.ownerClientId ? { ownerClientId: args.ownerClientId } : {}),
290
+ ...(args.event ? { event: args.event } : {}),
291
+ };
292
+ }
293
+
294
+ export function buildRetryRerouteDebugInfo(args: {
295
+ messageId: string;
296
+ accountId: string;
297
+ currentConnId: string;
298
+ decision: RetryRerouteDecision;
299
+ availableConnIds: string[];
300
+ }) {
301
+ if (args.decision.kind !== 'retry') {
302
+ return {
303
+ messageId: args.messageId,
304
+ accountId: args.accountId,
305
+ currentConnId: args.currentConnId,
306
+ availableConnIds: [...args.availableConnIds],
307
+ kind: args.decision.kind,
308
+ terminalReason: args.decision.terminalReason,
309
+ nextRetryCount: args.decision.nextRetryCount,
310
+ lastAttemptAt: args.decision.lastAttemptAt,
311
+ };
312
+ }
313
+
314
+ return {
315
+ messageId: args.messageId,
316
+ accountId: args.accountId,
317
+ currentConnId: args.currentConnId,
318
+ attemptedConnIds: [...args.decision.attemptedConnIds],
319
+ availableConnIds: [...args.availableConnIds],
320
+ revalidatedConnIds: [...args.decision.revalidatedConnIds],
321
+ hasUntriedAlternative: args.decision.hasUntriedAlternative,
322
+ shouldFastReroute: args.decision.shouldFastReroute,
323
+ routeAttemptRound: args.decision.routeAttemptRound,
324
+ nextAttemptAt: args.decision.nextAttemptAt,
325
+ fastReroutePending: args.decision.fastReroutePending,
326
+ nextRetryCount: args.decision.nextRetryCount,
327
+ lastAttemptAt: args.decision.lastAttemptAt,
328
+ lastError: args.decision.lastError,
329
+ kind: args.decision.kind,
330
+ };
331
+ }
332
+
333
+ export function buildPushFailureDebugInfo(args: {
334
+ messageId: string;
335
+ accountId: string;
336
+ retryCount: number;
337
+ lastError?: string;
338
+ retryable?: boolean;
339
+ kind?: string;
340
+ }) {
341
+ return {
342
+ messageId: args.messageId,
343
+ accountId: args.accountId,
344
+ ...(args.kind ? { kind: args.kind } : {}),
345
+ ...(typeof args.retryable === 'boolean' ? { retryable: args.retryable } : {}),
346
+ retryCount: args.retryCount,
347
+ error:
348
+ (typeof args.lastError === 'string' && args.lastError) || OUTBOUND_TERMINAL_REASON.PUSH_RETRY,
349
+ };
350
+ }
351
+
352
+ export function buildReplyMediaFallbackDebugInfo(args: {
353
+ sessionKey: string;
354
+ mediaUrl: string;
355
+ replyToId: string;
356
+ fallback: { text: string; reason: string };
357
+ }) {
358
+ return {
359
+ sessionKey: args.sessionKey,
360
+ mediaUrl: args.mediaUrl,
361
+ replyToId: args.replyToId || undefined,
362
+ fallbackText: args.fallback.text,
363
+ reason: args.fallback.reason,
364
+ };
365
+ }