@xmoxmo/bncr 0.5.6 → 0.5.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 (90) hide show
  1. package/dist/index.js +1 -9
  2. package/index.ts +1 -9
  3. package/package.json +1 -1
  4. package/scripts/check-pack.mjs +0 -8
  5. package/scripts/selfcheck.mjs +0 -10
  6. package/src/bootstrap/runtime-loader.ts +1 -1
  7. package/src/channel.ts +118 -171
  8. package/src/core/accounts.ts +1 -6
  9. package/src/core/connection-reachability.ts +2 -6
  10. package/src/core/dead-letter-diagnostics.ts +4 -10
  11. package/src/core/logging.ts +1 -1
  12. package/src/core/outbox-entry-builders.ts +78 -84
  13. package/src/core/outbox-file-transfer-failure.ts +5 -0
  14. package/src/core/outbox-file-transfer-guards.ts +6 -0
  15. package/src/core/outbox-file-transfer-prep.ts +8 -5
  16. package/src/core/outbox-file-transfer-success.ts +5 -0
  17. package/src/core/outbox-queue.ts +1 -7
  18. package/src/core/outbox-summary.ts +10 -56
  19. package/src/core/permissions.ts +2 -10
  20. package/src/core/persisted-outbox-entry.ts +54 -11
  21. package/src/core/policy.ts +1 -11
  22. package/src/core/register-trace.ts +4 -7
  23. package/src/core/status-meta.ts +1 -9
  24. package/src/core/status.ts +1 -21
  25. package/src/core/targets.ts +52 -70
  26. package/src/core/types.ts +1 -17
  27. package/src/core/value-sanitize.ts +38 -1
  28. package/src/messaging/inbound/commands.ts +2 -24
  29. package/src/messaging/inbound/dispatch-prep.ts +1 -1
  30. package/src/messaging/inbound/gate.ts +2 -31
  31. package/src/messaging/inbound/group-history.ts +0 -75
  32. package/src/messaging/inbound/parse.ts +2 -32
  33. package/src/messaging/inbound/scene-admin.ts +8 -8
  34. package/src/messaging/outbound/diagnostics-debug-builders.ts +0 -15
  35. package/src/messaging/outbound/diagnostics.ts +1 -7
  36. package/src/messaging/outbound/durable-queue-adapter.ts +17 -11
  37. package/src/messaging/outbound/marker-parser.ts +277 -28
  38. package/src/messaging/outbound/media-dedupe.ts +3 -10
  39. package/src/messaging/outbound/media.ts +1 -6
  40. package/src/messaging/outbound/normalize-outbound-send.ts +329 -0
  41. package/src/messaging/outbound/queue-selectors.ts +1 -5
  42. package/src/messaging/outbound/reply-enqueue-media.ts +1 -184
  43. package/src/messaging/outbound/reply-enqueue.ts +313 -175
  44. package/src/messaging/outbound/session-route.ts +6 -11
  45. package/src/messaging/outbound/target-resolver.ts +1 -6
  46. package/src/openclaw/media-runtime.ts +1 -1
  47. package/src/openclaw/routing-runtime.ts +0 -11
  48. package/src/openclaw/sdk-helpers.ts +0 -2
  49. package/src/plugin/bridge-media-facade.ts +0 -4
  50. package/src/plugin/bridge-outbox-facade.ts +0 -1
  51. package/src/plugin/channel-components.ts +0 -13
  52. package/src/plugin/channel-plugin-surface-group.ts +52 -65
  53. package/src/plugin/channel-runtime-builders-delivery.ts +3 -362
  54. package/src/plugin/channel-runtime-builders-status.ts +0 -160
  55. package/src/plugin/channel-runtime-builders.ts +3 -17
  56. package/src/plugin/channel-runtime-constants.ts +1 -1
  57. package/src/plugin/channel-runtime-types.ts +2 -24
  58. package/src/plugin/channel-send.ts +206 -221
  59. package/src/plugin/channel-utils.ts +8 -29
  60. package/src/plugin/connection-handlers-helpers.ts +0 -15
  61. package/src/plugin/connection-handlers.ts +6 -5
  62. package/src/plugin/file-inbound-abort.ts +5 -0
  63. package/src/plugin/file-inbound-chunk.ts +5 -0
  64. package/src/plugin/file-inbound-complete.ts +5 -0
  65. package/src/plugin/file-transfer-orchestrator.ts +39 -2
  66. package/src/plugin/media-dedupe-runtime.ts +1 -6
  67. package/src/plugin/media-orchestrators-runtime-group.ts +20 -149
  68. package/src/plugin/message-send.ts +7 -8
  69. package/src/plugin/messaging.ts +3 -3
  70. package/src/plugin/outbound.ts +6 -26
  71. package/src/plugin/outbox-drain-loop.ts +2 -2
  72. package/src/plugin/outbox-push.ts +1 -52
  73. package/src/plugin/outbox-unified-push-flow.ts +307 -0
  74. package/src/plugin/scene-registry.ts +1 -1
  75. package/src/plugin/target-runtime.ts +1 -6
  76. package/src/runtime/log-dedupe.ts +3 -3
  77. package/src/runtime/outbound-ack-timeout.ts +3 -12
  78. package/src/runtime/outbox-transitions.ts +1 -4
  79. package/src/runtime/status-snapshots.ts +2 -2
  80. package/src/runtime/status-worker.ts +3 -15
  81. package/src/core/outbox-text-push-prep.ts +0 -36
  82. package/src/messaging/outbound/build-send-action.ts +0 -130
  83. package/src/messaging/outbound/durable-message-adapter.ts +0 -121
  84. package/src/messaging/outbound/send-params.ts +0 -114
  85. package/src/messaging/outbound/send.ts +0 -127
  86. package/src/openclaw/ingress-runtime.ts +0 -25
  87. package/src/plugin/bridge-file-transfer-push-facade.ts +0 -142
  88. package/src/plugin/channel-send-runtime-group.ts +0 -37
  89. package/src/plugin/outbox-file-push-flow.ts +0 -69
  90. package/src/plugin/outbox-text-push-flow.ts +0 -90
@@ -193,7 +193,38 @@ export function createBncrFileTransferOrchestrator(runtime: BncrFileTransferOrch
193
193
  };
194
194
  const HTTP_URL_RE = /^https?:\/\//i;
195
195
 
196
- /** Try HEAD for Content-Type; fall back to Range+sniff for magic bytes when unclear. */
196
+ const EXT_TO_MIME: Record<string, string> = {
197
+ jpg: 'image/jpeg',
198
+ jpeg: 'image/jpeg',
199
+ png: 'image/png',
200
+ gif: 'image/gif',
201
+ webp: 'image/webp',
202
+ bmp: 'image/bmp',
203
+ heic: 'image/heic',
204
+ heif: 'image/heif',
205
+ mp4: 'video/mp4',
206
+ mov: 'video/quicktime',
207
+ mkv: 'video/x-matroska',
208
+ webm: 'video/webm',
209
+ avi: 'video/x-msvideo',
210
+ m4v: 'video/x-m4v',
211
+ mp3: 'audio/mpeg',
212
+ wav: 'audio/wav',
213
+ ogg: 'audio/ogg',
214
+ oga: 'audio/ogg',
215
+ opus: 'audio/opus',
216
+ m4a: 'audio/mp4',
217
+ aac: 'audio/aac',
218
+ flac: 'audio/flac',
219
+ };
220
+
221
+ function mimeFromUrlExt(url: string): string | undefined {
222
+ const clean = url.split(/[?#]/, 1)[0] || '';
223
+ const ext = clean.includes('.') ? clean.slice(clean.lastIndexOf('.') + 1).toLowerCase() : '';
224
+ return EXT_TO_MIME[ext];
225
+ }
226
+
227
+ /** Try HEAD for Content-Type; fall back to Range+sniff for magic bytes; then URL extension. */
197
228
  async function resolveRemoteMediaType(url: string): Promise<{ mimeType?: string }> {
198
229
  // 1) HEAD -> Content-Type
199
230
  let headerType = '';
@@ -229,8 +260,14 @@ export function createBncrFileTransferOrchestrator(runtime: BncrFileTransferOrch
229
260
  if (!sniffed) return {};
230
261
  return { mimeType: sniffed };
231
262
  } catch {
232
- return {};
263
+ /* Range sniff failed, fall through to extension inference */
233
264
  }
265
+
266
+ // 3) URL extension -> mime type (fallback when HEAD and Range both fail)
267
+ const extMime = mimeFromUrlExt(url);
268
+ if (extMime) return { mimeType: extMime };
269
+
270
+ return {};
234
271
  }
235
272
 
236
273
  /** Minimal magic-byte MIME sniffer for the first ~512 bytes. */
@@ -1,3 +1,4 @@
1
+ import { asString } from '../core/value-sanitize.ts';
1
2
  import {
2
3
  buildMediaTextFallback,
3
4
  type MediaDedupeCacheEntry,
@@ -5,12 +6,6 @@ import {
5
6
  normalizeReplyToId,
6
7
  } from '../messaging/outbound/media-dedupe.ts';
7
8
 
8
- function asString(v: unknown, fallback = ''): string {
9
- if (typeof v === 'string') return v;
10
- if (v == null) return fallback;
11
- return String(v);
12
- }
13
-
14
9
  export function createBncrMediaDedupeRuntime(runtime: {
15
10
  now: () => number;
16
11
  recentMediaDedupeBySession: Map<string, Map<string, MediaDedupeCacheEntry>>;
@@ -1,21 +1,13 @@
1
1
  import type { BncrRoute, FileSendTransferState, OutboxEntry } from '../core/types.ts';
2
- import { normalizeMessageText } from '../messaging/outbound/media-dedupe.ts';
3
2
  import type {
4
3
  NormalizedReplyPayload,
5
4
  OutboundReplyTargetPolicy,
6
- ReplyMediaEntriesParams,
7
5
  ReplyPayloadInput,
8
6
  } from '../messaging/outbound/reply-enqueue.ts';
9
7
  import {
10
8
  enqueueNormalizedReplyPayload,
11
- enqueueReplyTextEntry,
12
9
  normalizeReplyPayload,
13
10
  } from '../messaging/outbound/reply-enqueue.ts';
14
- import {
15
- enqueueReplyMediaFallbackTextEntry,
16
- enqueueReplyMediaFileTransferEntry,
17
- enqueueSingleReplyMediaEntry,
18
- } from '../messaging/outbound/reply-enqueue-media.ts';
19
11
  import type { createBncrFileAckRuntime } from './file-ack-runtime.ts';
20
12
  import { createBncrFileTransferOrchestrator } from './file-transfer-orchestrator.ts';
21
13
  import type { createBncrFileTransferSetup } from './file-transfer-setup.ts';
@@ -27,92 +19,12 @@ type FileAckPayload = Awaited<
27
19
  ReturnType<ReturnType<typeof createBncrFileAckRuntime>['waitForFileAck']>
28
20
  >;
29
21
 
30
- function buildReplyMediaEntryHelpers(runtime: {
31
- logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
32
- enqueueOutbound: (entry: OutboxEntry) => void;
33
- buildTextOutboxEntry: (args: {
34
- accountId: string;
35
- sessionKey: string;
36
- route: BncrRoute;
37
- text: string;
38
- kind?: 'tool' | 'block' | 'final';
39
- replyToId?: string;
40
- replyTargetPolicy?: OutboundReplyTargetPolicy;
41
- }) => OutboxEntry;
42
- buildFileTransferOutboxEntry: (args: {
43
- accountId: string;
44
- sessionKey: string;
45
- route: BncrRoute;
46
- mediaUrl: string;
47
- mediaLocalRoots?: readonly string[];
48
- text: string;
49
- asVoice: boolean;
50
- audioAsVoice: boolean;
51
- type?: string;
52
- extra?: Record<string, unknown>;
53
- kind?: 'tool' | 'block' | 'final';
54
- replyToId?: string;
55
- replyTargetPolicy?: OutboundReplyTargetPolicy;
56
- downloadMedia?: boolean;
57
- }) => OutboxEntry;
58
- rememberRecentMediaSend: (args: {
59
- sessionKey: string;
60
- mediaUrl: string;
61
- text: string;
62
- replyToId: string;
63
- createdAt: number;
64
- }) => void;
65
- }) {
66
- return {
67
- enqueueReplyMediaFallbackTextEntry: (
68
- params: Parameters<typeof enqueueReplyMediaFallbackTextEntry>[0],
69
- ) =>
70
- enqueueReplyMediaFallbackTextEntry(params, {
71
- logInfo: runtime.logInfo,
72
- enqueueOutbound: runtime.enqueueOutbound,
73
- buildTextOutboxEntry: runtime.buildTextOutboxEntry,
74
- }),
75
- enqueueReplyMediaFileTransferEntry: (
76
- params: Parameters<typeof enqueueReplyMediaFileTransferEntry>[0],
77
- ) =>
78
- enqueueReplyMediaFileTransferEntry(params, {
79
- enqueueOutbound: runtime.enqueueOutbound,
80
- buildFileTransferOutboxEntry: runtime.buildFileTransferOutboxEntry,
81
- rememberRecentMediaSend: runtime.rememberRecentMediaSend,
82
- }),
83
- };
84
- }
85
-
86
- function buildReplyTextEntryHelper(runtime: {
87
- enqueueOutbound: (entry: OutboxEntry) => void;
88
- buildTextOutboxEntry: (args: {
89
- accountId: string;
90
- sessionKey: string;
91
- route: BncrRoute;
92
- text: string;
93
- kind?: 'tool' | 'block' | 'final';
94
- replyToId?: string;
95
- replyTargetPolicy?: OutboundReplyTargetPolicy;
96
- }) => OutboxEntry;
97
- }) {
98
- return (params: {
99
- accountId: string;
100
- sessionKey: string;
101
- route: BncrRoute;
102
- payload: NormalizedReplyPayload;
103
- }) =>
104
- enqueueReplyTextEntry(params, {
105
- enqueueOutbound: runtime.enqueueOutbound,
106
- buildTextOutboxEntry: runtime.buildTextOutboxEntry,
107
- });
108
- }
109
-
110
- function normalizeReplyOrchestratorPayload(args: {
22
+ async function normalizeReplyOrchestratorPayload(args: {
111
23
  payload: ReplyPayloadInput;
112
24
  asString: (value: unknown, fallback?: string) => string;
113
25
  replyTargetPolicy?: OutboundReplyTargetPolicy;
114
26
  }) {
115
- return normalizeReplyPayload(
27
+ return await normalizeReplyPayload(
116
28
  args.payload,
117
29
  { asString: args.asString },
118
30
  {
@@ -186,29 +98,21 @@ export function createBncrMediaOrchestratorsRuntimeGroup(runtime: {
186
98
  payload: NormalizedReplyPayload;
187
99
  }) => void;
188
100
  enqueueOutbound: (entry: OutboxEntry) => void;
189
- buildTextOutboxEntry: (args: {
101
+ buildOutboxEntry: (args: {
190
102
  accountId: string;
191
103
  sessionKey: string;
192
104
  route: BncrRoute;
193
- text: string;
194
- kind?: 'tool' | 'block' | 'final';
195
- replyToId?: string;
196
- replyTargetPolicy?: OutboundReplyTargetPolicy;
197
- }) => OutboxEntry;
198
- buildFileTransferOutboxEntry: (args: {
199
- accountId: string;
200
- sessionKey: string;
201
- route: BncrRoute;
202
- mediaUrl: string;
203
- mediaLocalRoots?: readonly string[];
204
- text: string;
205
- asVoice: boolean;
206
- audioAsVoice: boolean;
207
105
  type?: string;
208
- extra?: Record<string, unknown>;
106
+ msg: string;
209
107
  kind?: 'tool' | 'block' | 'final';
210
108
  replyToId?: string;
211
109
  replyTargetPolicy?: OutboundReplyTargetPolicy;
110
+ extra?: Record<string, unknown>;
111
+ transferMode?: 'text' | 'media';
112
+ mediaUrl?: string;
113
+ mediaLocalRoots?: readonly string[];
114
+ asVoice?: boolean;
115
+ audioAsVoice?: boolean;
212
116
  downloadMedia?: boolean;
213
117
  }) => OutboxEntry;
214
118
  rememberRecentMediaSend: (args: {
@@ -244,40 +148,7 @@ export function createBncrMediaOrchestratorsRuntimeGroup(runtime: {
244
148
  logFileTransferCompleteAck: runtime.logFileTransferCompleteAck,
245
149
  });
246
150
 
247
- const replyMediaEntryHelpers = buildReplyMediaEntryHelpers(runtime);
248
- const replyTextEntryHelper = buildReplyTextEntryHelper(runtime);
249
-
250
- const enqueueReplyMediaEntries = (params: ReplyMediaEntriesParams) => {
251
- let first = true;
252
- const currentTime = runtime.now();
253
-
254
- for (const mediaUrl of params.payload.mediaList) {
255
- const normalizedText = normalizeMessageText(first ? params.payload.text : '');
256
- const fallback = runtime.tryBuildMediaDedupeFallback({
257
- sessionKey: params.sessionKey,
258
- mediaUrl,
259
- text: normalizedText,
260
- replyToId: params.payload.replyToId,
261
- currentTime,
262
- });
263
-
264
- enqueueSingleReplyMediaEntry(
265
- {
266
- params,
267
- mediaUrl,
268
- normalizedText,
269
- text: first ? params.payload.text : '',
270
- fallback,
271
- currentTime,
272
- },
273
- replyMediaEntryHelpers,
274
- );
275
-
276
- first = false;
277
- }
278
- };
279
-
280
- const enqueueFromReply = (params: {
151
+ const enqueueFromReply = async (params: {
281
152
  accountId: string;
282
153
  sessionKey: string;
283
154
  route: BncrRoute;
@@ -286,7 +157,7 @@ export function createBncrMediaOrchestratorsRuntimeGroup(runtime: {
286
157
  replyTargetPolicy?: OutboundReplyTargetPolicy;
287
158
  }) => {
288
159
  const { accountId, sessionKey, route, payload, mediaLocalRoots, replyTargetPolicy } = params;
289
- const normalized = normalizeReplyOrchestratorPayload({
160
+ const normalized = await normalizeReplyOrchestratorPayload({
290
161
  payload,
291
162
  asString: runtime.asString,
292
163
  replyTargetPolicy,
@@ -302,19 +173,19 @@ export function createBncrMediaOrchestratorsRuntimeGroup(runtime: {
302
173
  },
303
174
  {
304
175
  logEnqueueFromReply: runtime.logEnqueueFromReply,
305
- enqueueReplyMediaEntries,
306
- enqueueReplyTextEntry: replyTextEntryHelper,
176
+ enqueueOutbound: runtime.enqueueOutbound,
177
+ buildOutboxEntry: runtime.buildOutboxEntry,
178
+ tryBuildMediaDedupeFallback: runtime.tryBuildMediaDedupeFallback,
179
+ rememberRecentMediaSend: runtime.rememberRecentMediaSend,
180
+ logInfo: runtime.logInfo,
307
181
  },
308
182
  );
309
183
  };
310
184
 
311
- const replyMediaOrchestrator = {
312
- enqueueFromReply,
313
- enqueueReplyMediaEntries,
314
- };
315
-
316
185
  return {
317
186
  fileTransferOrchestrator,
318
- replyMediaOrchestrator,
187
+ replyMediaOrchestrator: {
188
+ enqueueFromReply,
189
+ },
319
190
  };
320
191
  }
@@ -1,23 +1,22 @@
1
1
  import type { ChannelMessageSendResult } from 'openclaw/plugin-sdk/channel-message';
2
-
3
- type MessageSendContext = Record<string, unknown>;
2
+ import type { BncrChannelSendContext } from './channel-runtime-types.ts';
4
3
 
5
4
  export type BncrMessageSendBridge = {
6
5
  channelMessageSendText: (
7
- ctx: MessageSendContext,
6
+ ctx: BncrChannelSendContext,
8
7
  ) => ChannelMessageSendResult | Promise<ChannelMessageSendResult>;
9
8
  channelMessageSendMedia: (
10
- ctx: MessageSendContext,
9
+ ctx: BncrChannelSendContext,
11
10
  ) => ChannelMessageSendResult | Promise<ChannelMessageSendResult>;
12
11
  channelMessageSendPayload: (
13
- ctx: MessageSendContext,
12
+ ctx: BncrChannelSendContext,
14
13
  ) => ChannelMessageSendResult | Promise<ChannelMessageSendResult>;
15
14
  };
16
15
 
17
16
  export function createBncrMessageSend(getBridge: () => BncrMessageSendBridge) {
18
17
  return {
19
- text: async (ctx: MessageSendContext) => getBridge().channelMessageSendText(ctx),
20
- media: async (ctx: MessageSendContext) => getBridge().channelMessageSendMedia(ctx),
21
- payload: async (ctx: MessageSendContext) => getBridge().channelMessageSendPayload(ctx),
18
+ text: async (ctx: BncrChannelSendContext) => getBridge().channelMessageSendText(ctx),
19
+ media: async (ctx: BncrChannelSendContext) => getBridge().channelMessageSendMedia(ctx),
20
+ payload: async (ctx: BncrChannelSendContext) => getBridge().channelMessageSendPayload(ctx),
22
21
  };
23
22
  }
@@ -67,12 +67,12 @@ function isDisplayTarget(
67
67
  return Boolean(value && typeof value === 'object');
68
68
  }
69
69
 
70
- export function normalizeBncrMessagingTarget(raw: string) {
70
+ function normalizeBncrMessagingTarget(raw: string) {
71
71
  const input = asSanitizedString(raw).trim();
72
72
  return input || undefined;
73
73
  }
74
74
 
75
- export function formatBncrMessagingTargetDisplay({ target }: BncrMessagingTargetDisplayInput) {
75
+ function formatBncrMessagingTargetDisplay({ target }: BncrMessagingTargetDisplayInput) {
76
76
  if (typeof target === 'string') {
77
77
  const parsed = parseExplicitTarget(asSanitizedString(target).trim());
78
78
  return parsed?.route ? formatBncrHumanDisplay(parsed.route) : asSanitizedString(target).trim();
@@ -183,7 +183,7 @@ export function createBncrMessagingSurface(getBridge: () => BncrMessagingRuntime
183
183
  };
184
184
  }
185
185
 
186
- export function createBncrMessagingTargetResolver(getBridge: () => BncrMessagingRuntimeBridge) {
186
+ function createBncrMessagingTargetResolver(getBridge: () => BncrMessagingRuntimeBridge) {
187
187
  return {
188
188
  looksLikeId: (raw: string, normalized?: string) => {
189
189
  return looksLikeBncrExplicitTarget(asSanitizedString(normalized || raw).trim());
@@ -1,35 +1,15 @@
1
1
  import { normalizeAccountId } from '../core/accounts.ts';
2
+ import { asString } from '../core/value-sanitize.ts';
2
3
  import {
3
4
  deleteBncrMessageAction,
4
5
  editBncrMessageAction,
5
6
  reactBncrMessageAction,
6
7
  sendBncrReplyAction,
7
8
  } from '../messaging/outbound/actions.ts';
8
-
9
- function asString(v: unknown, fallback = ''): string {
10
- return typeof v === 'string' ? v : v == null ? fallback : String(v);
11
- }
9
+ import type { BncrChannelSendContext } from './channel-runtime-types.ts';
12
10
 
13
11
  type BncrOutboundDeliveryResult = { channel: string; messageId: string; chatId: string };
14
12
 
15
- type BncrOutboundSendContext = {
16
- accountId?: string | null;
17
- to?: string;
18
- text?: string;
19
- mediaUrl?: string;
20
- type?: string;
21
- kind?: string;
22
- replyToId?: string | null;
23
- replyToMessageId?: string | null;
24
- asVoice?: boolean;
25
- audioAsVoice?: boolean;
26
- mediaLocalRoots?: readonly string[];
27
- /** Pass-through fields from host — bncr plugin does NOT consume these. */
28
- forceDocument?: boolean;
29
- gifPlayback?: boolean;
30
- silent?: boolean;
31
- };
32
-
33
13
  type BncrOutboundReplyActionContext = {
34
14
  accountId?: string | null;
35
15
  to?: string;
@@ -54,18 +34,18 @@ type BncrOutboundEditActionContext = BncrOutboundTargetActionContext & {
54
34
 
55
35
  export type BncrOutboundBridge = {
56
36
  channelSendText: (
57
- ctx: BncrOutboundSendContext,
37
+ ctx: BncrChannelSendContext,
58
38
  ) => BncrOutboundDeliveryResult | Promise<BncrOutboundDeliveryResult>;
59
39
  channelSendMedia: (
60
- ctx: BncrOutboundSendContext,
40
+ ctx: BncrChannelSendContext,
61
41
  ) => BncrOutboundDeliveryResult | Promise<BncrOutboundDeliveryResult>;
62
42
  };
63
43
 
64
44
  export function createBncrOutboundRuntime(getBridge: () => BncrOutboundBridge) {
65
45
  return {
66
46
  deliveryMode: 'gateway' as const,
67
- sendText: async (ctx: BncrOutboundSendContext) => getBridge().channelSendText(ctx),
68
- sendMedia: async (ctx: BncrOutboundSendContext) => getBridge().channelSendMedia(ctx),
47
+ sendText: async (ctx: BncrChannelSendContext) => getBridge().channelSendText(ctx),
48
+ sendMedia: async (ctx: BncrChannelSendContext) => getBridge().channelSendMedia(ctx),
69
49
  replyAction: async (ctx: BncrOutboundReplyActionContext) =>
70
50
  sendBncrReplyAction({
71
51
  accountId: normalizeAccountId(ctx?.accountId),
@@ -295,8 +295,8 @@ export function createBncrOutboxDrainLoop(
295
295
  try {
296
296
  pushed = await runtime.tryPushEntry(entry);
297
297
  } catch (error) {
298
- const meta = runtime.isPlainObject(entry.payload?._meta) ? entry.payload._meta : null;
299
- if (meta?.kind === 'file-transfer') {
298
+ const msg = runtime.isPlainObject(entry.payload?.message) ? entry.payload.message : null;
299
+ if (msg?.transferMode === 'media') {
300
300
  runtime.handleFileTransferPushFailure({ entry, error });
301
301
  } else {
302
302
  runtime.handleTextPushFailure({ entry, error });
@@ -3,12 +3,7 @@ import {
3
3
  resolveFileTransferFailureState,
4
4
  } from '../core/outbox-file-transfer-failure.ts';
5
5
  import { buildTextPushFailureArgs } from '../core/outbox-text-push-failure.ts';
6
- import {
7
- buildTextPushBroadcastPayload,
8
- buildTextPushOkArgs,
9
- buildTextPushRouteSelectArgs,
10
- buildTextPushSuccessArgs,
11
- } from '../core/outbox-text-push-success.ts';
6
+
12
7
  import type { BncrConnection, OutboxEntry } from '../core/types.ts';
13
8
  import {
14
9
  buildOutboxPushOkDebugInfo,
@@ -137,51 +132,6 @@ export function createBncrOutboxPush(runtime: BncrOutboxPushRuntime) {
137
132
  );
138
133
  };
139
134
 
140
- const pushTextSuccessPath = (args: {
141
- entry: OutboxEntry;
142
- owner: BncrConnection | null;
143
- connIds: Iterable<string>;
144
- recentInboundReachable: boolean;
145
- routeReason: string;
146
- ownerConnId?: string;
147
- }) => {
148
- runtime.gatewayBroadcastToConnIds(
149
- runtime.pushEvent,
150
- buildTextPushBroadcastPayload({
151
- payload: args.entry.payload,
152
- messageId: args.entry.messageId,
153
- }),
154
- new Set(args.connIds),
155
- );
156
- logOutboxRouteSelect(
157
- buildTextPushRouteSelectArgs({
158
- entry: args.entry,
159
- connIds: args.connIds,
160
- routeReason: args.routeReason,
161
- recentInboundReachable: args.recentInboundReachable,
162
- owner: args.owner,
163
- event: runtime.pushEvent,
164
- }),
165
- );
166
- runtime.recordOutboxPushSuccess(
167
- buildTextPushSuccessArgs({
168
- entry: args.entry,
169
- connIds: args.connIds,
170
- ownerConnId: args.ownerConnId,
171
- ownerClientId: args.ownerConnId ? args.owner?.clientId : undefined,
172
- }),
173
- );
174
- logOutboxPushOkSummary(args.entry.messageId);
175
- logOutboxPushOk(
176
- buildTextPushOkArgs({
177
- entry: args.entry,
178
- connIds: args.connIds,
179
- recentInboundReachable: args.recentInboundReachable,
180
- event: runtime.pushEvent,
181
- }),
182
- );
183
- };
184
-
185
135
  const handleTextPushFailure = (args: { entry: OutboxEntry; error: unknown }) => {
186
136
  runtime.recordOutboxPushFailure({
187
137
  entry: args.entry,
@@ -253,7 +203,6 @@ export function createBncrOutboxPush(runtime: BncrOutboxPushRuntime) {
253
203
  };
254
204
 
255
205
  return {
256
- pushTextSuccessPath,
257
206
  handleTextPushFailure,
258
207
  handleFileTransferPushFailure,
259
208
  handleFileTransferPushGuardFailure,