@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
@@ -0,0 +1,289 @@
1
+ import path from 'node:path';
2
+ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/core';
3
+ import { emitBncrLogLine } from '../core/logging.ts';
4
+ import { formatDisplayScope, normalizeStoredSessionKey, parseRouteLike } from '../core/targets.ts';
5
+ import type { BncrRoute } from '../core/types.ts';
6
+ import type { BncrInboundParamsInput } from '../messaging/inbound/contracts.ts';
7
+ import { dispatchBncrInbound } from '../messaging/inbound/dispatch.ts';
8
+ import { parseBncrInboundParams } from '../messaging/inbound/parse.ts';
9
+ import { OUTBOUND_FLUSH_REASON, OUTBOUND_FLUSH_TRIGGER } from '../messaging/outbound/reasons.ts';
10
+ import type { ReplyPayloadInput } from '../messaging/outbound/reply-enqueue.ts';
11
+ import { getOpenClawRuntimeConfig } from '../openclaw/config-runtime.ts';
12
+ import { saveOpenClawChannelMediaBuffer } from '../openclaw/media-runtime.ts';
13
+ import { createBncrChannelSendRuntime } from './channel-send.ts';
14
+ import { createBncrConnectionHandlers } from './connection-handlers.ts';
15
+ import { createBncrDiagnosticsHandlers } from './diagnostics-handlers.ts';
16
+ import { createBncrFileInboundHandlers } from './file-inbound-handlers.ts';
17
+ import { createBncrInboundHandlers } from './inbound-handlers.ts';
18
+
19
+ export function createBncrConnectionHandlersComponent(
20
+ runtime: Parameters<typeof createBncrConnectionHandlers>[0],
21
+ ) {
22
+ return createBncrConnectionHandlers(runtime);
23
+ }
24
+
25
+ export function createBncrDiagnosticsHandlersComponent(
26
+ runtime: Parameters<typeof createBncrDiagnosticsHandlers>[0],
27
+ ) {
28
+ return createBncrDiagnosticsHandlers(runtime);
29
+ }
30
+
31
+ export function createBncrBridgeDiagnosticsHandlersComponent(runtime: {
32
+ getApi: () => OpenClawPluginApi;
33
+ channelId: string;
34
+ asString: (value: unknown, fallback?: string) => string;
35
+ now: () => number;
36
+ countInvalidOutboxSessionKeys: Parameters<
37
+ typeof createBncrDiagnosticsHandlers
38
+ >[0]['countInvalidOutboxSessionKeys'];
39
+ countLegacyAccountResidue: Parameters<
40
+ typeof createBncrDiagnosticsHandlers
41
+ >[0]['countLegacyAccountResidue'];
42
+ buildRuntimeStatusInput: Parameters<
43
+ typeof createBncrDiagnosticsHandlers
44
+ >[0]['buildRuntimeStatusInput'];
45
+ getAccountRuntimeSnapshot: Parameters<
46
+ typeof createBncrDiagnosticsHandlers
47
+ >[0]['getAccountRuntimeSnapshot'];
48
+ buildIntegratedDiagnostics: Parameters<
49
+ typeof createBncrDiagnosticsHandlers
50
+ >[0]['buildIntegratedDiagnostics'];
51
+ buildExtendedDiagnostics: Parameters<
52
+ typeof createBncrDiagnosticsHandlers
53
+ >[0]['buildExtendedDiagnostics'];
54
+ buildDownlinkHealth: Parameters<typeof createBncrDiagnosticsHandlers>[0]['buildDownlinkHealth'];
55
+ buildRuntimeFlags: Parameters<typeof createBncrDiagnosticsHandlers>[0]['buildRuntimeFlags'];
56
+ activeConnectionCount: Parameters<
57
+ typeof createBncrDiagnosticsHandlers
58
+ >[0]['activeConnectionCount'];
59
+ getMessageAckWaiterCount: Parameters<
60
+ typeof createBncrDiagnosticsHandlers
61
+ >[0]['getMessageAckWaiterCount'];
62
+ getFileAckWaiterCount: Parameters<
63
+ typeof createBncrDiagnosticsHandlers
64
+ >[0]['getFileAckWaiterCount'];
65
+ filterDeadLetterEntries: Parameters<
66
+ typeof createBncrDiagnosticsHandlers
67
+ >[0]['filterDeadLetterEntries'];
68
+ listDeadLetterEntries: Parameters<
69
+ typeof createBncrDiagnosticsHandlers
70
+ >[0]['listDeadLetterEntries'];
71
+ buildDeadLetterDiagnostics: Parameters<
72
+ typeof createBncrDiagnosticsHandlers
73
+ >[0]['buildDeadLetterDiagnostics'];
74
+ replaceDeadLetterEntries: Parameters<
75
+ typeof createBncrDiagnosticsHandlers
76
+ >[0]['replaceDeadLetterEntries'];
77
+ scheduleSave: Parameters<typeof createBncrDiagnosticsHandlers>[0]['scheduleSave'];
78
+ logDeadLetterSummary: Parameters<typeof createBncrDiagnosticsHandlers>[0]['logDeadLetterSummary'];
79
+ pluginRoot: string;
80
+ }) {
81
+ const buildDiagnosticsRuntimeStatusInput = (
82
+ accountId: string,
83
+ overrides: {
84
+ running: boolean;
85
+ invalidOutboxSessionKeys?: number;
86
+ legacyAccountResidue?: number;
87
+ },
88
+ ) => ({
89
+ ...runtime.buildRuntimeStatusInput(accountId, overrides),
90
+ running: overrides.running,
91
+ channelRoot: resolveBncrChannelRoot(runtime.pluginRoot),
92
+ });
93
+
94
+ return createBncrDiagnosticsHandlers({
95
+ getApi: runtime.getApi,
96
+ channelId: runtime.channelId,
97
+ asString: runtime.asString,
98
+ now: runtime.now,
99
+ countInvalidOutboxSessionKeys: runtime.countInvalidOutboxSessionKeys,
100
+ countLegacyAccountResidue: runtime.countLegacyAccountResidue,
101
+ buildRuntimeStatusInput: buildDiagnosticsRuntimeStatusInput,
102
+ getAccountRuntimeSnapshot: (accountId, runtimeStatusInput) =>
103
+ runtime.getAccountRuntimeSnapshot(
104
+ accountId,
105
+ runtimeStatusInput || buildDiagnosticsRuntimeStatusInput(accountId, { running: true }),
106
+ ),
107
+ buildIntegratedDiagnostics: (accountId, runtimeStatusInput) =>
108
+ runtime.buildIntegratedDiagnostics(
109
+ accountId,
110
+ runtimeStatusInput || buildDiagnosticsRuntimeStatusInput(accountId, { running: true }),
111
+ ),
112
+ buildExtendedDiagnostics: (accountId, args) =>
113
+ runtime.buildExtendedDiagnostics(accountId, {
114
+ runtimeStatusInput:
115
+ args?.runtimeStatusInput ||
116
+ buildDiagnosticsRuntimeStatusInput(accountId, { running: true }),
117
+ integratedDiagnostics:
118
+ args?.integratedDiagnostics ||
119
+ runtime.buildIntegratedDiagnostics(
120
+ accountId,
121
+ buildDiagnosticsRuntimeStatusInput(accountId, { running: true }),
122
+ ),
123
+ }),
124
+ buildDownlinkHealth: runtime.buildDownlinkHealth,
125
+ buildRuntimeFlags: runtime.buildRuntimeFlags,
126
+ activeConnectionCount: runtime.activeConnectionCount,
127
+ getMessageAckWaiterCount: runtime.getMessageAckWaiterCount,
128
+ getFileAckWaiterCount: runtime.getFileAckWaiterCount,
129
+ filterDeadLetterEntries: runtime.filterDeadLetterEntries,
130
+ listDeadLetterEntries: runtime.listDeadLetterEntries,
131
+ buildDeadLetterDiagnostics: runtime.buildDeadLetterDiagnostics,
132
+ replaceDeadLetterEntries: runtime.replaceDeadLetterEntries,
133
+ scheduleSave: runtime.scheduleSave,
134
+ logDeadLetterSummary: runtime.logDeadLetterSummary,
135
+ });
136
+ }
137
+
138
+ export function createBncrFileInboundHandlersComponent(runtime: {
139
+ getApi: () => OpenClawPluginApi;
140
+ asString: (value: unknown, fallback?: string) => string;
141
+ now: () => number;
142
+ normalizeAccountId: (accountId: string) => string;
143
+ finiteNonNegativeNumberOrNull: (value: unknown) => number | null;
144
+ shouldIgnoreStaleEvent: Parameters<
145
+ typeof createBncrFileInboundHandlers
146
+ >[0]['shouldIgnoreStaleEvent'];
147
+ observeLease: Parameters<typeof createBncrFileInboundHandlers>[0]['observeLease'];
148
+ matchesTransferOwner: Parameters<typeof createBncrFileInboundHandlers>[0]['matchesTransferOwner'];
149
+ refreshAcceptedFileTransferLiveState: Parameters<
150
+ typeof createBncrFileInboundHandlers
151
+ >[0]['refreshAcceptedFileTransferLiveState'];
152
+ logWarn: Parameters<typeof createBncrFileInboundHandlers>[0]['logWarn'];
153
+ fileRecvTransfers: Parameters<typeof createBncrFileInboundHandlers>[0]['fileRecvTransfers'];
154
+ inboundFileTransferMaxBytes: number;
155
+ inboundFileTransferMaxChunks: number;
156
+ }) {
157
+ return createBncrFileInboundHandlers({
158
+ asString: runtime.asString,
159
+ now: runtime.now,
160
+ normalizeAccountId: runtime.normalizeAccountId,
161
+ finiteNonNegativeNumberOrNull: runtime.finiteNonNegativeNumberOrNull,
162
+ shouldIgnoreStaleEvent: runtime.shouldIgnoreStaleEvent,
163
+ observeLease: runtime.observeLease,
164
+ matchesTransferOwner: runtime.matchesTransferOwner,
165
+ refreshAcceptedFileTransferLiveState: runtime.refreshAcceptedFileTransferLiveState,
166
+ logWarn: runtime.logWarn,
167
+ parseRouteLike,
168
+ normalizeStoredSessionKey,
169
+ saveInboundMediaBuffer: async ({ buffer, mimeType, fileName }) =>
170
+ await saveOpenClawChannelMediaBuffer(
171
+ runtime.getApi(),
172
+ buffer,
173
+ mimeType,
174
+ 'inbound',
175
+ 50 * 1024 * 1024,
176
+ fileName,
177
+ ),
178
+ fileRecvTransfers: runtime.fileRecvTransfers,
179
+ inboundFileTransferMaxBytes: runtime.inboundFileTransferMaxBytes,
180
+ inboundFileTransferMaxChunks: runtime.inboundFileTransferMaxChunks,
181
+ });
182
+ }
183
+
184
+ export function createBncrInboundHandlersComponent(runtime: {
185
+ getApi: () => OpenClawPluginApi;
186
+ channelId: string;
187
+ bridgeId: string;
188
+ pluginRoot: string;
189
+ asString: (value: unknown, fallback?: string) => string;
190
+ now: () => number;
191
+ syncDebugFlag: () => Promise<void>;
192
+ shouldIgnoreStaleEvent: Parameters<typeof createBncrInboundHandlers>[0]['shouldIgnoreStaleEvent'];
193
+ buildInboundResponsePayload: Parameters<
194
+ typeof createBncrInboundHandlers
195
+ >[0]['buildInboundResponsePayload'];
196
+ refreshLiveConnectionState: Parameters<
197
+ typeof createBncrInboundHandlers
198
+ >[0]['refreshLiveConnectionState'];
199
+ logInfo: Parameters<typeof createBncrInboundHandlers>[0]['logInfo'];
200
+ logError: Parameters<typeof createBncrInboundHandlers>[0]['logError'];
201
+ buildInboundAcceptedLifecycleDebugInfo: Parameters<
202
+ typeof createBncrInboundHandlers
203
+ >[0]['buildInboundAcceptedLifecycleDebugInfo'];
204
+ isOnline: (accountId: string) => boolean;
205
+ hasRecentInboundReachability: (accountId: string) => boolean;
206
+ getActiveConnectionKey: (accountId: string) => string | null;
207
+ buildActiveConnectionDebugList: Parameters<
208
+ typeof createBncrInboundHandlers
209
+ >[0]['buildActiveConnectionDebugList'];
210
+ markLastInboundAt: (accountId: string) => void;
211
+ ensureCanonicalAgentId: Parameters<typeof createBncrInboundHandlers>[0]['ensureCanonicalAgentId'];
212
+ prepareInboundAcceptance: Parameters<
213
+ typeof createBncrInboundHandlers
214
+ >[0]['prepareInboundAcceptance'];
215
+ logInboundSummary: Parameters<typeof createBncrInboundHandlers>[0]['logInboundSummary'];
216
+ flushPushQueueBestEffort: (args: {
217
+ accountId?: string;
218
+ trigger?: string;
219
+ reason?: string;
220
+ }) => void;
221
+ rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
222
+ enqueueFromReply: (args: {
223
+ accountId: string;
224
+ sessionKey: string;
225
+ route: BncrRoute;
226
+ payload: ReplyPayloadInput;
227
+ mediaLocalRoots?: readonly string[];
228
+ }) => Promise<void>;
229
+ setInboundActivity: (accountId: string, at: number) => void;
230
+ scheduleSave: () => void;
231
+ }) {
232
+ return createBncrInboundHandlers({
233
+ channelId: runtime.channelId,
234
+ bridgeId: runtime.bridgeId,
235
+ asString: runtime.asString,
236
+ now: runtime.now,
237
+ syncDebugFlag: runtime.syncDebugFlag,
238
+ parseInboundParams: (params: unknown) =>
239
+ parseBncrInboundParams(params as BncrInboundParamsInput),
240
+ shouldIgnoreStaleEvent: runtime.shouldIgnoreStaleEvent,
241
+ buildInboundResponsePayload: runtime.buildInboundResponsePayload,
242
+ refreshLiveConnectionState: runtime.refreshLiveConnectionState,
243
+ logInfo: runtime.logInfo,
244
+ logError: runtime.logError,
245
+ buildInboundAcceptedLifecycleDebugInfo: runtime.buildInboundAcceptedLifecycleDebugInfo,
246
+ isOnline: runtime.isOnline,
247
+ hasRecentInboundReachability: runtime.hasRecentInboundReachability,
248
+ getActiveConnectionKey: runtime.getActiveConnectionKey,
249
+ buildActiveConnectionDebugList: runtime.buildActiveConnectionDebugList,
250
+ markLastInboundAt: runtime.markLastInboundAt,
251
+ getConfig: () => getOpenClawRuntimeConfig(runtime.getApi()),
252
+ ensureCanonicalAgentId: runtime.ensureCanonicalAgentId,
253
+ prepareInboundAcceptance: runtime.prepareInboundAcceptance,
254
+ formatDisplayScope,
255
+ logInboundSummary: runtime.logInboundSummary,
256
+ flushOnInboundAccepted: (accountId) =>
257
+ runtime.flushPushQueueBestEffort({
258
+ accountId,
259
+ trigger: OUTBOUND_FLUSH_TRIGGER.INBOUND,
260
+ reason: OUTBOUND_FLUSH_REASON.INBOUND_ACCEPTED,
261
+ }),
262
+ dispatchInbound: ({ cfg, parsed, canonicalAgentId }) =>
263
+ dispatchBncrInbound({
264
+ api: runtime.getApi(),
265
+ channelId: runtime.channelId,
266
+ cfg,
267
+ parsed,
268
+ canonicalAgentId,
269
+ rememberSessionRoute: runtime.rememberSessionRoute,
270
+ enqueueFromReply: runtime.enqueueFromReply,
271
+ setInboundActivity: runtime.setInboundActivity,
272
+ scheduleSave: runtime.scheduleSave,
273
+ logger: {
274
+ warn: (msg: string) => emitBncrLogLine('warn', msg),
275
+ error: (msg: string) => emitBncrLogLine('error', msg),
276
+ },
277
+ }),
278
+ });
279
+ }
280
+
281
+ export function createBncrChannelSendRuntimeComponent(
282
+ runtime: Parameters<typeof createBncrChannelSendRuntime>[0],
283
+ ) {
284
+ return createBncrChannelSendRuntime(runtime);
285
+ }
286
+
287
+ export function resolveBncrChannelRoot(pluginRoot: string) {
288
+ return pluginRoot || path.join(process.cwd(), 'plugins', 'bncr');
289
+ }
@@ -0,0 +1,149 @@
1
+ import { CHANNEL_ID } from '../core/accounts.ts';
2
+ import { normalizeInboundSessionKey, withTaskSessionKey } from '../core/targets.ts';
3
+ import type { BncrRoute } from '../core/types.ts';
4
+ import type { OpenClawResolvedAgentRoute } from '../openclaw/routing-runtime.ts';
5
+ import type { BncrChannelConfigRoot } from './channel-runtime-types.ts';
6
+
7
+ function assertResolvedSessionKey(
8
+ resolvedRoute: OpenClawResolvedAgentRoute,
9
+ ): OpenClawResolvedAgentRoute & { sessionKey: string } {
10
+ if (typeof resolvedRoute.sessionKey !== 'string' || !resolvedRoute.sessionKey.trim()) {
11
+ throw new Error('OpenClaw resolveAgentRoute returned empty sessionKey');
12
+ }
13
+ return { ...resolvedRoute, sessionKey: resolvedRoute.sessionKey };
14
+ }
15
+
16
+ export function buildInboundAcceptedLifecycleDebugInfo(args: {
17
+ stage: 'accepted';
18
+ bridge: string;
19
+ accountId: string;
20
+ connId: string;
21
+ clientId?: string;
22
+ outboundReady: boolean;
23
+ preferredForOutbound: boolean;
24
+ inboundOnly: boolean;
25
+ onlineAfterSeen: boolean;
26
+ recentInboundReachable: boolean;
27
+ activeConnectionKey: string | null;
28
+ activeConnections: Array<{
29
+ connId: string;
30
+ clientId?: string;
31
+ connectedAt: number;
32
+ lastSeenAt: number;
33
+ }>;
34
+ }) {
35
+ return {
36
+ stage: args.stage,
37
+ bridge: args.bridge,
38
+ accountId: args.accountId,
39
+ connId: args.connId,
40
+ clientId: args.clientId,
41
+ outboundReady: args.outboundReady,
42
+ preferredForOutbound: args.preferredForOutbound,
43
+ inboundOnly: args.inboundOnly,
44
+ onlineAfterSeen: args.onlineAfterSeen,
45
+ recentInboundReachable: args.recentInboundReachable,
46
+ activeConnectionKey: args.activeConnectionKey,
47
+ activeConnections: args.activeConnections,
48
+ };
49
+ }
50
+
51
+ export function resolveInboundSessionContext(args: {
52
+ cfg: BncrChannelConfigRoot;
53
+ accountId: string;
54
+ peer: { kind: string } & Record<string, unknown>;
55
+ route: BncrRoute;
56
+ sessionKeyFromRoute?: string;
57
+ canonicalAgentId: string;
58
+ taskKey?: string;
59
+ text: string;
60
+ extractedText?: string;
61
+ asString: (value: unknown, fallback?: string) => string;
62
+ resolveAgentRoute: (params: {
63
+ cfg: BncrChannelConfigRoot;
64
+ channel: string;
65
+ accountId: string;
66
+ peer: unknown;
67
+ }) => OpenClawResolvedAgentRoute;
68
+ }) {
69
+ const resolvedRoute = assertResolvedSessionKey(
70
+ args.resolveAgentRoute({
71
+ cfg: args.cfg,
72
+ channel: CHANNEL_ID,
73
+ accountId: args.accountId,
74
+ peer: args.peer,
75
+ }),
76
+ );
77
+ const baseSessionKey =
78
+ normalizeInboundSessionKey(
79
+ args.sessionKeyFromRoute || '',
80
+ args.route,
81
+ args.canonicalAgentId || '',
82
+ ) || resolvedRoute.sessionKey;
83
+ const taskSessionKey = withTaskSessionKey(baseSessionKey, args.taskKey);
84
+ return {
85
+ resolvedRoute,
86
+ baseSessionKey,
87
+ taskSessionKey,
88
+ sessionKey: taskSessionKey || baseSessionKey,
89
+ inboundText: args.asString(args.extractedText || args.text || ''),
90
+ };
91
+ }
92
+
93
+ export function buildInboundResponsePayload(
94
+ args:
95
+ | { kind: 'stale-ignored'; accountId: string; msgId?: string | null }
96
+ | { kind: 'invalid-peer' }
97
+ | { kind: 'duplicated'; accountId: string; msgId?: string | null }
98
+ | { kind: 'gate-denied'; accountId: string; msgId?: string | null; reason: string }
99
+ | {
100
+ kind: 'accepted';
101
+ accountId: string;
102
+ sessionKey: string;
103
+ msgId?: string | null;
104
+ taskKey?: string | null;
105
+ }
106
+ | { kind: 'invalid-session'; accountId: string; msgId?: string | null },
107
+ ) {
108
+ switch (args.kind) {
109
+ case 'stale-ignored':
110
+ return {
111
+ accepted: false,
112
+ stale: true,
113
+ ignored: true,
114
+ accountId: args.accountId,
115
+ msgId: args.msgId ?? null,
116
+ };
117
+ case 'invalid-peer':
118
+ return { error: 'platform/groupId/userId required' };
119
+ case 'duplicated':
120
+ return {
121
+ accepted: true,
122
+ duplicated: true,
123
+ accountId: args.accountId,
124
+ msgId: args.msgId ?? null,
125
+ };
126
+ case 'gate-denied':
127
+ return {
128
+ accepted: false,
129
+ accountId: args.accountId,
130
+ msgId: args.msgId ?? null,
131
+ reason: args.reason,
132
+ };
133
+ case 'accepted':
134
+ return {
135
+ accepted: true,
136
+ accountId: args.accountId,
137
+ sessionKey: args.sessionKey,
138
+ msgId: args.msgId ?? null,
139
+ taskKey: args.taskKey ?? null,
140
+ };
141
+ case 'invalid-session':
142
+ return {
143
+ accepted: false,
144
+ accountId: args.accountId,
145
+ invalidSession: true,
146
+ msgId: args.msgId ?? null,
147
+ };
148
+ }
149
+ }
@@ -0,0 +1,129 @@
1
+ import type { ChannelMessageSendResult } from 'openclaw/plugin-sdk/channel-message';
2
+ import type { BncrRoute } from '../core/types.ts';
3
+ import type { ReplyPayloadInput } from '../messaging/outbound/reply-enqueue.ts';
4
+ import type { OutboundReplyTargetPolicy } from '../messaging/outbound/reply-target-policy.ts';
5
+ import type {
6
+ BncrChannelConfigRoot,
7
+ BncrChannelSendContext,
8
+ BncrStatusRuntimeSnapshot,
9
+ BncrVerifiedTarget,
10
+ } from './channel-runtime-types.ts';
11
+ import type { BncrOutboundBridge } from './outbound.ts';
12
+
13
+ export type BncrChannelPluginBridge = {
14
+ channelMessageSendText: (
15
+ ctx: BncrChannelSendContext,
16
+ ) => ChannelMessageSendResult | Promise<ChannelMessageSendResult>;
17
+ channelMessageSendMedia: (
18
+ ctx: BncrChannelSendContext,
19
+ ) => ChannelMessageSendResult | Promise<ChannelMessageSendResult>;
20
+ channelMessageSendPayload: (
21
+ ctx: BncrChannelSendContext,
22
+ ) => ChannelMessageSendResult | Promise<ChannelMessageSendResult>;
23
+ channelSendText: BncrOutboundBridge['channelSendText'];
24
+ channelSendMedia: BncrOutboundBridge['channelSendMedia'];
25
+ ensureCanonicalAgentId: (args: {
26
+ cfg: BncrChannelConfigRoot;
27
+ accountId: string;
28
+ channelId: string;
29
+ peer: { kind: 'direct'; id: string };
30
+ }) => string;
31
+ resolveRouteBySession: (raw: string, accountId: string) => BncrRoute | null;
32
+ getChannelSummary: (defaultAccountId: string) => Record<string, unknown>;
33
+ getAccountRuntimeSnapshot: (accountId: string) => BncrStatusRuntimeSnapshot;
34
+ getStatusHeadline: (accountId: string) => string;
35
+ resolveVerifiedTarget: (to: string, accountId: string) => BncrVerifiedTarget;
36
+ rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
37
+ enqueueFromReply: (args: {
38
+ accountId: string;
39
+ sessionKey: string;
40
+ route: BncrRoute;
41
+ payload: ReplyPayloadInput;
42
+ mediaLocalRoots?: readonly string[];
43
+ replyTargetPolicy?: OutboundReplyTargetPolicy;
44
+ }) => Promise<void>;
45
+ channelStartAccount: (ctx: unknown) => void | Promise<void>;
46
+ channelStopAccount: (ctx: unknown) => void | Promise<void>;
47
+ };
48
+
49
+ export function createBncrChannelPluginBridgeGroup(runtime: {
50
+ channelId: string;
51
+ defaultAccountId: string;
52
+ getBridge: () => BncrChannelPluginBridge;
53
+ }) {
54
+ const getMessageSendBridge = () => {
55
+ const bridge = runtime.getBridge();
56
+ return {
57
+ channelMessageSendText: (ctx: BncrChannelSendContext) => bridge.channelMessageSendText(ctx),
58
+ channelMessageSendMedia: (ctx: BncrChannelSendContext) => bridge.channelMessageSendMedia(ctx),
59
+ channelMessageSendPayload: (ctx: BncrChannelSendContext) =>
60
+ bridge.channelMessageSendPayload(ctx),
61
+ };
62
+ };
63
+
64
+ const getOutboundBridge = () => {
65
+ const bridge = runtime.getBridge();
66
+ return {
67
+ channelSendText: (ctx: Parameters<BncrOutboundBridge['channelSendText']>[0]) =>
68
+ bridge.channelSendText(ctx),
69
+ channelSendMedia: (ctx: Parameters<BncrOutboundBridge['channelSendMedia']>[0]) =>
70
+ bridge.channelSendMedia(ctx),
71
+ };
72
+ };
73
+
74
+ const getMessagingBridge = () => {
75
+ const bridge = runtime.getBridge();
76
+ return {
77
+ canonicalAgentId: undefined,
78
+ ensureCanonicalAgentId: (params: { cfg: BncrChannelConfigRoot; accountId: string }) =>
79
+ bridge.ensureCanonicalAgentId({
80
+ cfg: params.cfg,
81
+ accountId: params.accountId,
82
+ channelId: runtime.channelId,
83
+ peer: { kind: 'direct', id: params.accountId },
84
+ }),
85
+ resolveRouteBySession: (raw: string, accountId: string) =>
86
+ bridge.resolveRouteBySession(raw, accountId),
87
+ };
88
+ };
89
+
90
+ const getStatusBridge = () => {
91
+ const bridge = runtime.getBridge();
92
+ return {
93
+ getChannelSummary: (defaultAccountId: string) => bridge.getChannelSummary(defaultAccountId),
94
+ getAccountRuntimeSnapshot: (accountId?: string) =>
95
+ bridge.getAccountRuntimeSnapshot(accountId || runtime.defaultAccountId),
96
+ getStatusHeadline: (accountId?: string) =>
97
+ bridge.getStatusHeadline(accountId || runtime.defaultAccountId),
98
+ };
99
+ };
100
+
101
+ const getToolActionBridge = () => {
102
+ const bridge = runtime.getBridge();
103
+ return {
104
+ resolveVerifiedTarget: (to: string, accountId: string) =>
105
+ bridge.resolveVerifiedTarget(to, accountId),
106
+ rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) =>
107
+ bridge.rememberSessionRoute(sessionKey, accountId, route),
108
+ enqueueFromReply: (args: Parameters<BncrChannelPluginBridge['enqueueFromReply']>[0]) =>
109
+ bridge.enqueueFromReply(args),
110
+ };
111
+ };
112
+
113
+ const getGatewayBridge = () => {
114
+ const bridge = runtime.getBridge();
115
+ return {
116
+ channelStartAccount: (ctx: unknown) => bridge.channelStartAccount(ctx),
117
+ channelStopAccount: (ctx: unknown) => bridge.channelStopAccount(ctx),
118
+ };
119
+ };
120
+
121
+ return {
122
+ getMessageSendBridge,
123
+ getOutboundBridge,
124
+ getMessagingBridge,
125
+ getStatusBridge,
126
+ getToolActionBridge,
127
+ getGatewayBridge,
128
+ };
129
+ }