@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.
- package/README.md +5 -0
- package/dist/index.js +28 -5
- package/index.ts +55 -721
- package/package.json +8 -4
- package/scripts/check-pack.mjs +93 -18
- package/scripts/check-register-drift.mjs +35 -13
- package/scripts/selfcheck.mjs +80 -11
- package/src/bootstrap/channel-plugin-runtime.ts +81 -0
- package/src/bootstrap/cli.ts +97 -0
- package/src/bootstrap/register-runtime-gateway.ts +129 -0
- package/src/bootstrap/register-runtime-helpers.ts +140 -0
- package/src/bootstrap/register-runtime-singleton.ts +137 -0
- package/src/bootstrap/register-runtime.ts +201 -0
- package/src/bootstrap/runtime-discovery.ts +187 -0
- package/src/bootstrap/runtime-loader.ts +54 -0
- package/src/channel.ts +1590 -4967
- package/src/core/accounts.ts +23 -4
- package/src/core/dead-letter-diagnostics.ts +37 -5
- package/src/core/diagnostics.ts +31 -15
- package/src/core/downlink-health.ts +3 -11
- package/src/core/extended-diagnostics.ts +78 -36
- package/src/core/file-transfer-payloads.ts +1 -1
- package/src/core/logging.ts +1 -0
- package/src/core/outbox-enqueue.ts +13 -2
- package/src/core/outbox-entry-builders.ts +2 -0
- package/src/core/outbox-summary.ts +75 -3
- package/src/core/permissions.ts +15 -2
- package/src/core/persisted-outbox-entry.ts +21 -6
- package/src/core/policy.ts +45 -4
- package/src/core/probe.ts +3 -15
- package/src/core/register-trace.ts +3 -3
- package/src/core/status.ts +43 -4
- package/src/core/targets.ts +216 -205
- package/src/core/types.ts +221 -0
- package/src/core/value-sanitize.ts +29 -0
- package/src/messaging/inbound/commands.ts +147 -172
- package/src/messaging/inbound/context-facts.ts +4 -2
- package/src/messaging/inbound/contracts.ts +70 -0
- package/src/messaging/inbound/dispatch-prep.ts +303 -0
- package/src/messaging/inbound/dispatch.ts +49 -462
- package/src/messaging/inbound/gate.ts +18 -5
- package/src/messaging/inbound/last-route.ts +10 -4
- package/src/messaging/inbound/media-url-download.ts +109 -0
- package/src/messaging/inbound/native-command-runtime.ts +225 -0
- package/src/messaging/inbound/parse.ts +2 -1
- package/src/messaging/inbound/remote-media.ts +49 -0
- package/src/messaging/inbound/reply-config.ts +16 -4
- package/src/messaging/inbound/reply-dispatch.ts +162 -0
- package/src/messaging/inbound/runtime-compat.ts +31 -10
- package/src/messaging/inbound/session-label.ts +15 -7
- package/src/messaging/inbound/turn-context.ts +131 -0
- package/src/messaging/outbound/actions.ts +24 -10
- package/src/messaging/outbound/diagnostics-debug-builders.ts +365 -0
- package/src/messaging/outbound/diagnostics.ts +31 -355
- package/src/messaging/outbound/durable-message-adapter.ts +20 -16
- package/src/messaging/outbound/durable-queue-adapter.ts +20 -7
- package/src/messaging/outbound/media.ts +24 -13
- package/src/messaging/outbound/reply-enqueue-media.ts +181 -0
- package/src/messaging/outbound/reply-enqueue.ts +57 -134
- package/src/messaging/outbound/send-params.ts +3 -0
- package/src/messaging/outbound/send.ts +19 -10
- package/src/messaging/outbound/session-route.ts +18 -3
- package/src/openclaw/channel-runtime-contracts.ts +76 -0
- package/src/openclaw/config-runtime.ts +13 -7
- package/src/openclaw/inbound-session-runtime.ts +7 -3
- package/src/openclaw/ingress-runtime.ts +17 -27
- package/src/openclaw/reply-runtime.ts +54 -59
- package/src/openclaw/routing-runtime.ts +35 -18
- package/src/openclaw/runtime-surface.ts +156 -12
- package/src/openclaw/sdk-helpers.ts +8 -1
- package/src/openclaw/session-route-runtime.ts +12 -12
- package/src/plugin/ack-outbox-runtime-group.ts +264 -0
- package/src/plugin/bridge-ack-facade.ts +137 -0
- package/src/plugin/bridge-connection-facade.ts +111 -0
- package/src/plugin/bridge-diagnostics-facade.ts +23 -0
- package/src/plugin/bridge-drain-facade.ts +98 -0
- package/src/plugin/bridge-extended-diagnostics-facade.ts +149 -0
- package/src/plugin/bridge-file-transfer-push-facade.ts +140 -0
- package/src/plugin/bridge-lifecycle.ts +156 -0
- package/src/plugin/bridge-media-facade.ts +241 -0
- package/src/plugin/bridge-outbox-facade.ts +182 -0
- package/src/plugin/bridge-runtime-helpers.ts +266 -0
- package/src/plugin/bridge-runtime-snapshots.ts +104 -0
- package/src/plugin/bridge-runtime-surface-facade.ts +8 -0
- package/src/plugin/bridge-status-facade.ts +76 -0
- package/src/plugin/bridge-status-worker-facade.ts +72 -0
- package/src/plugin/bridge-support-runtime.ts +137 -0
- package/src/plugin/bridge-surface-handlers-group.ts +242 -0
- package/src/plugin/bridge-surface-helpers.ts +28 -0
- package/src/plugin/capabilities.ts +1 -3
- package/src/plugin/channel-components.ts +289 -0
- package/src/plugin/channel-inbound-helpers.ts +149 -0
- package/src/plugin/channel-plugin-bridge-group.ts +129 -0
- package/src/plugin/channel-plugin-surface-group.ts +202 -0
- package/src/plugin/channel-runtime-builders-delivery.ts +513 -0
- package/src/plugin/channel-runtime-builders-status.ts +331 -0
- package/src/plugin/channel-runtime-builders.ts +25 -0
- package/src/plugin/channel-runtime-constants.ts +40 -0
- package/src/plugin/channel-runtime-types.ts +146 -0
- package/src/plugin/channel-send-runtime-group.ts +37 -0
- package/src/plugin/channel-send.ts +226 -0
- package/src/plugin/channel-utils.ts +102 -0
- package/src/plugin/config.ts +24 -3
- package/src/plugin/connection-handlers-helpers.ts +254 -0
- package/src/plugin/connection-handlers.ts +440 -0
- package/src/plugin/connection-state-helpers.ts +159 -0
- package/src/plugin/connection-state-runtime-group.ts +51 -0
- package/src/plugin/connection-state.ts +527 -0
- package/src/plugin/diagnostics-handlers.ts +211 -0
- package/src/plugin/error-message.ts +15 -0
- package/src/plugin/file-ack-runtime.ts +284 -0
- package/src/plugin/file-inbound-abort.ts +112 -0
- package/src/plugin/file-inbound-chunk.ts +146 -0
- package/src/plugin/file-inbound-complete.ts +153 -0
- package/src/plugin/file-inbound-handlers.ts +19 -0
- package/src/plugin/file-inbound-init.ts +122 -0
- package/src/plugin/file-inbound-runtime.ts +51 -0
- package/src/plugin/file-inbound-state.ts +62 -0
- package/src/plugin/file-transfer-logs.ts +227 -0
- package/src/plugin/file-transfer-orchestrator-chunk.ts +135 -0
- package/src/plugin/file-transfer-orchestrator.ts +304 -0
- package/src/plugin/file-transfer-runtime-group.ts +102 -0
- package/src/plugin/file-transfer-send.ts +89 -0
- package/src/plugin/file-transfer-setup.ts +206 -0
- package/src/plugin/gateway-event-context.ts +41 -0
- package/src/plugin/gateway-runtime.ts +14 -4
- package/src/plugin/inbound-acceptance.ts +107 -0
- package/src/plugin/inbound-handlers.ts +248 -0
- package/src/plugin/inbound-surface-handlers-group.ts +152 -0
- package/src/plugin/media-dedupe-runtime.ts +90 -0
- package/src/plugin/media-orchestrators-runtime-group.ts +316 -0
- package/src/plugin/message-ack-runtime.ts +284 -0
- package/src/plugin/message-send.ts +16 -6
- package/src/plugin/messaging.ts +98 -36
- package/src/plugin/outbound.ts +50 -8
- package/src/plugin/outbox-ack-logs.ts +136 -0
- package/src/plugin/outbox-ack-outcome.ts +128 -0
- package/src/plugin/outbox-drain-ack.ts +145 -0
- package/src/plugin/outbox-drain-failure.ts +84 -0
- package/src/plugin/outbox-drain-loop.ts +554 -0
- package/src/plugin/outbox-drain-post-push.ts +159 -0
- package/src/plugin/outbox-drain-runtime.ts +141 -0
- package/src/plugin/outbox-drain-schedule.ts +116 -0
- package/src/plugin/outbox-file-push-flow.ts +69 -0
- package/src/plugin/outbox-push-route-runtime-group.ts +81 -0
- package/src/plugin/outbox-push.ts +267 -0
- package/src/plugin/outbox-route.ts +181 -0
- package/src/plugin/outbox-text-push-flow.ts +90 -0
- package/src/plugin/runtime-diagnostics-assembler.ts +183 -0
- package/src/plugin/runtime-diagnostics-helpers.ts +302 -0
- package/src/plugin/runtime-diagnostics-payload-builders.ts +171 -0
- package/src/plugin/runtime-diagnostics-snapshot.ts +31 -0
- package/src/plugin/setup.ts +33 -6
- package/src/plugin/state-store.ts +249 -0
- package/src/plugin/state-transient-runtime-group.ts +105 -0
- package/src/plugin/status-runtime.ts +251 -0
- package/src/plugin/status.ts +33 -7
- package/src/plugin/target-runtime.ts +141 -0
- package/src/plugin/target-status-runtime-group.ts +130 -0
- package/src/plugin/transient-state-runtime.ts +82 -0
- package/src/runtime/outbound-ack-timeout.ts +5 -3
- package/src/runtime/outbound-flags.ts +24 -8
- package/src/runtime/status-snapshots.ts +36 -7
- package/src/runtime/status-worker.ts +34 -4
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
18
|
-
enqueueText: (
|
|
18
|
+
export type BncrDurableMessageQueuedAdapterDeps = {
|
|
19
|
+
enqueueText: (
|
|
20
|
+
ctx: ChannelMessageSendTextContext<OpenClawConfig>,
|
|
21
|
+
) => Promise<OutboxEntry> | OutboxEntry;
|
|
19
22
|
enqueueMedia?: (
|
|
20
|
-
ctx: ChannelMessageSendMediaContext<
|
|
23
|
+
ctx: ChannelMessageSendMediaContext<OpenClawConfig>,
|
|
21
24
|
) => Promise<OutboxEntry> | OutboxEntry;
|
|
22
25
|
enqueuePayload?: (
|
|
23
|
-
ctx: ChannelMessageSendPayloadContext<
|
|
26
|
+
ctx: ChannelMessageSendPayloadContext<OpenClawConfig>,
|
|
24
27
|
) => Promise<OutboxEntry> | OutboxEntry;
|
|
25
28
|
now?: () => number;
|
|
26
29
|
};
|
|
27
30
|
|
|
28
|
-
export type BncrDurableMessageQueuedAdapterBuilderDeps
|
|
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<
|
|
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
|
|
45
|
-
deps: BncrDurableMessageQueuedAdapterDeps
|
|
46
|
-
): ChannelMessageAdapterShape<
|
|
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
|
|
66
|
-
deps: BncrDurableMessageQueuedAdapterBuilderDeps
|
|
67
|
-
): ChannelMessageAdapterShape<
|
|
68
|
-
return createBncrDurableMessageQueuedAdapter
|
|
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 ??
|
|
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 ??
|
|
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
|
|
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
|
|
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
|
|
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
|
|
147
|
-
|
|
148
|
-
|
|
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 (
|
|
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
|
-
}):
|
|
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
|
|
36
|
-
return major as any;
|
|
48
|
+
if (isMimeMajorOutboundMessageType(major)) return major;
|
|
37
49
|
return 'file';
|
|
38
50
|
}
|
|
39
|
-
if (isStandard) return hinted
|
|
40
|
-
if (major
|
|
41
|
-
return major as any;
|
|
51
|
+
if (isStandard) return hinted;
|
|
52
|
+
if (isMimeMajorOutboundMessageType(major)) return major;
|
|
42
53
|
return 'file';
|
|
43
54
|
}
|
|
44
55
|
|