@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
@@ -1,4 +1,3 @@
1
- import { buildChannelOutboundSessionRoute } from 'openclaw/plugin-sdk/core';
2
1
  import { resolveInboundLastRouteSessionKey } from 'openclaw/plugin-sdk/routing';
3
2
  import type { BncrChannelConfigRoot } from '../plugin/channel-runtime-types.ts';
4
3
  import type {
@@ -17,10 +16,6 @@ type OpenClawRoutingApi = {
17
16
  }) => OpenClawResolvedAgentRoute;
18
17
  };
19
18
 
20
- type BuildChannelOutboundSessionRouteParams = Parameters<
21
- typeof buildChannelOutboundSessionRoute
22
- >[0];
23
-
24
19
  function resolveRoutingApi(api: OpenClawChannelRuntimeApiHolder): OpenClawRoutingApi {
25
20
  const routing = api?.runtime?.channel?.routing;
26
21
  if (!routing || typeof routing !== 'object') {
@@ -56,9 +51,3 @@ export function resolveOpenClawInboundLastRouteSessionKey(params: {
56
51
  },
57
52
  );
58
53
  }
59
-
60
- export function buildOpenClawChannelOutboundSessionRoute(
61
- params: BuildChannelOutboundSessionRouteParams,
62
- ) {
63
- return buildChannelOutboundSessionRoute(params);
64
- }
@@ -1,4 +1,3 @@
1
- import { readBooleanParam as sdkReadBooleanParam } from 'openclaw/plugin-sdk/boolean-param';
2
1
  import {
3
2
  applyAccountNameToChannelSection as sdkApplyAccountNameToChannelSection,
4
3
  jsonResult as sdkJsonResult,
@@ -15,7 +14,6 @@ import { extractToolSend as sdkExtractToolSend } from 'openclaw/plugin-sdk/tool-
15
14
 
16
15
  export type OpenClawChannelToolSend = ChannelToolSend;
17
16
 
18
- export const readOpenClawBooleanParam = sdkReadBooleanParam;
19
17
  export const readOpenClawStringParam = sdkReadStringParam;
20
18
  export const readOpenClawJsonFileWithFallback = sdkReadJsonFileWithFallback;
21
19
  export const writeOpenClawJsonFileAtomically = sdkWriteJsonFileAtomically;
@@ -2,7 +2,6 @@ import type { BncrRoute, FileSendTransferState } from '../core/types.ts';
2
2
  import type {
3
3
  NormalizedReplyPayload,
4
4
  OutboundReplyTargetPolicy,
5
- ReplyMediaEntriesParams,
6
5
  ReplyPayloadInput,
7
6
  } from '../messaging/outbound/reply-enqueue.ts';
8
7
  import type { OpenClawLoadedMedia } from '../openclaw/media-runtime.ts';
@@ -61,7 +60,6 @@ export function createBncrBridgeMediaFacade(runtime: {
61
60
  mediaLocalRoots?: readonly string[];
62
61
  replyTargetPolicy?: OutboundReplyTargetPolicy;
63
62
  }) => void;
64
- enqueueReplyMediaEntries: (params: ReplyMediaEntriesParams) => void;
65
63
  };
66
64
  logInfoJson: (
67
65
  scope: string | undefined,
@@ -161,7 +159,6 @@ export function createBncrBridgeMediaFacade(runtime: {
161
159
  const loadOutboundTransferMedia = async (params: {
162
160
  mediaUrl: string;
163
161
  mediaLocalRoots?: readonly string[];
164
- downloadMedia?: boolean;
165
162
  }): Promise<{
166
163
  loaded: OpenClawLoadedMedia;
167
164
  size: number;
@@ -211,7 +208,6 @@ export function createBncrBridgeMediaFacade(runtime: {
211
208
  waitCompleteAck: runtime.fileTransferOrchestrator.waitCompleteAck,
212
209
  transferMediaToBncrClient: runtime.fileTransferOrchestrator.transferMediaToBncrClient,
213
210
  enqueueFromReply: runtime.replyMediaOrchestrator.enqueueFromReply,
214
- enqueueReplyMediaEntries: runtime.replyMediaOrchestrator.enqueueReplyMediaEntries,
215
211
  logEnqueueFromReply,
216
212
  logFileChunkDiag: runtime.fileTransferLogs.logFileChunkDiag,
217
213
  logFileTransferStart: runtime.fileTransferLogs.logFileTransferStart,
@@ -126,7 +126,6 @@ export function createBncrBridgeOutboxFacade(runtime: {
126
126
  ),
127
127
  { debugOnly: true },
128
128
  );
129
- runtime.logOutboundSummary(entry);
130
129
  const accountId = runtime.normalizeAccountId(entry.accountId);
131
130
  runtime.incrementCounter(runtime.outboundEnqueueCountByAccount, accountId);
132
131
  runtime.lastOutboundEnqueueAtByAccount.set(accountId, runtime.now());
@@ -11,7 +11,6 @@ import { OUTBOUND_FLUSH_REASON, OUTBOUND_FLUSH_TRIGGER } from '../messaging/outb
11
11
  import type { ReplyPayloadInput } from '../messaging/outbound/reply-enqueue.ts';
12
12
  import { getOpenClawRuntimeConfig } from '../openclaw/config-runtime.ts';
13
13
  import { saveOpenClawChannelMediaBuffer } from '../openclaw/media-runtime.ts';
14
- import { createBncrChannelSendRuntime } from './channel-send.ts';
15
14
  import { createBncrConnectionHandlers } from './connection-handlers.ts';
16
15
  import { createBncrDiagnosticsHandlers } from './diagnostics-handlers.ts';
17
16
  import { createBncrFileInboundHandlers } from './file-inbound-handlers.ts';
@@ -23,12 +22,6 @@ export function createBncrConnectionHandlersComponent(
23
22
  return createBncrConnectionHandlers(runtime);
24
23
  }
25
24
 
26
- export function createBncrDiagnosticsHandlersComponent(
27
- runtime: Parameters<typeof createBncrDiagnosticsHandlers>[0],
28
- ) {
29
- return createBncrDiagnosticsHandlers(runtime);
30
- }
31
-
32
25
  export function createBncrBridgeDiagnosticsHandlersComponent(runtime: {
33
26
  getApi: () => OpenClawPluginApi;
34
27
  channelId: string;
@@ -312,12 +305,6 @@ export function createBncrInboundHandlersComponent(runtime: {
312
305
  });
313
306
  }
314
307
 
315
- export function createBncrChannelSendRuntimeComponent(
316
- runtime: Parameters<typeof createBncrChannelSendRuntime>[0],
317
- ) {
318
- return createBncrChannelSendRuntime(runtime);
319
- }
320
-
321
308
  export function resolveBncrChannelRoot(pluginRoot: string) {
322
309
  return pluginRoot || path.join(process.cwd(), 'plugins', 'bncr');
323
310
  }
@@ -1,15 +1,14 @@
1
- import { randomUUID } from 'node:crypto';
2
1
  import type { ChannelMessageSendResult } from 'openclaw/plugin-sdk/channel-message';
3
- import { listAccountIds, resolveAccount } from '../core/accounts.ts';
2
+ import { listAccountIds, normalizeAccountId, resolveAccount } from '../core/accounts.ts';
4
3
  import { BncrConfigSchema } from '../core/config-schema.ts';
5
4
  import { resolveBncrChannelPolicy } from '../core/policy.ts';
6
5
  import type { ReplyPayloadInput } from '../messaging/outbound/reply-enqueue.ts';
7
6
  import type { OutboundReplyTargetPolicy } from '../messaging/outbound/reply-target-policy.ts';
8
- import { sendBncrMedia, sendBncrText } from '../messaging/outbound/send.ts';
9
- import { normalizeBncrSendParams } from '../messaging/outbound/send-params.ts';
10
7
  import type { OpenClawChannelToolSend, openClawJsonResult } from '../openclaw/sdk-helpers.ts';
8
+ import { readOpenClawStringParam } from '../openclaw/sdk-helpers.ts';
11
9
  import type {
12
10
  BncrChannelConfigRoot,
11
+ BncrChannelSendContext,
13
12
  BncrSceneRecord,
14
13
  BncrStatusRuntimeSnapshot,
15
14
  BncrVerifiedTarget,
@@ -139,71 +138,59 @@ export function createBncrChannelPluginSurfaceGroup(runtime: {
139
138
  if (action !== 'send') {
140
139
  throw new Error(`Action ${action} is not supported for provider ${runtime.channelId}.`);
141
140
  }
142
- const normalized = normalizeBncrSendParams({ params, accountId: accountId || '' });
143
- console.log(
144
- '[bncr] handleAction normalized.downloadMedia=' +
145
- JSON.stringify(normalized.downloadMedia) +
146
- '|mediaUrl=' +
147
- JSON.stringify(normalized.mediaUrl) +
148
- '|hasMedia=' +
149
- Boolean(normalized.mediaUrl || normalized.mediaUrls?.length),
141
+
142
+ /* -- extract raw params (no normalization) ----------------------- */
143
+ const paramsObj = (params && typeof params === 'object' ? params : {}) as Record<
144
+ string,
145
+ unknown
146
+ >;
147
+ const to = readOpenClawStringParam(paramsObj, 'to', { required: true });
148
+ const resolvedAccountId = normalizeAccountId(
149
+ (readOpenClawStringParam(paramsObj, 'accountId') ?? accountId) || '',
150
150
  );
151
+ const toolMediaUrl =
152
+ (typeof paramsObj.mediaUrl === 'string' ? paramsObj.mediaUrl : '') ||
153
+ (typeof paramsObj.media === 'string' ? paramsObj.media : '') ||
154
+ (typeof paramsObj.path === 'string' ? paramsObj.path : '') ||
155
+ (typeof paramsObj.filePath === 'string' ? paramsObj.filePath : '');
156
+ const rawExtra = paramsObj.extra;
157
+ const extra =
158
+ rawExtra && typeof rawExtra === 'object' && !Array.isArray(rawExtra)
159
+ ? (rawExtra as Record<string, unknown>)
160
+ : undefined;
161
+ const rawMediaUrls = paramsObj.mediaUrls;
162
+ const mediaUrls = Array.isArray(rawMediaUrls)
163
+ ? rawMediaUrls.filter((u): u is string => typeof u === 'string')
164
+ : undefined;
165
+
166
+ const toolMessage = readOpenClawStringParam(paramsObj, 'message', { allowEmpty: true }) ?? '';
167
+ const toolCaption = readOpenClawStringParam(paramsObj, 'caption', { allowEmpty: true }) ?? '';
168
+ const text = toolCaption || toolMessage || '';
169
+ const ctx: BncrChannelSendContext = {
170
+ to,
171
+ accountId: resolvedAccountId,
172
+ text,
173
+ mediaUrl: toolMediaUrl || undefined,
174
+ mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
175
+ asVoice: paramsObj.asVoice === true,
176
+ audioAsVoice: paramsObj.audioAsVoice === true,
177
+ downloadMedia: paramsObj.downloadMedia as boolean | undefined,
178
+ type: typeof paramsObj.type === 'string' ? paramsObj.type : undefined,
179
+ extra,
180
+ forceDocument: paramsObj.forceDocument === true,
181
+ gifPlayback: paramsObj.gifPlayback === true,
182
+ silent: paramsObj.silent === true,
183
+ mediaLocalRoots,
184
+ };
151
185
 
152
- // Priority: marker → scene config → global default → false
153
- if (normalized.downloadMedia === undefined) {
154
- const parts = normalized.to.split(':');
155
- if (parts.length >= 3) {
156
- const sceneKey = `${parts[1]}:${parts[2]}`;
157
- const scene = runtime.sceneRegistry.get(sceneKey);
158
- if (scene?.downloadMedia === true) {
159
- normalized.downloadMedia = true;
160
- }
161
- if (normalized.downloadMedia === undefined) {
162
- const globalScene = runtime.sceneRegistry.get('__global__');
163
- if (globalScene?.downloadMedia === true) {
164
- normalized.downloadMedia = true;
165
- }
166
- }
167
- }
186
+ const hasContent = Boolean(text?.trim() || toolMediaUrl || mediaUrls?.length || extra);
187
+ if (!hasContent) {
188
+ throw new Error('send requires message, media, or extra params');
168
189
  }
169
190
 
170
- const toolActionBridge = runtime.getToolActionBridge();
171
- const result =
172
- normalized.mediaUrl || normalized.mediaUrls?.length
173
- ? await sendBncrMedia({
174
- channelId: runtime.channelId,
175
- accountId: normalized.accountId,
176
- to: normalized.to,
177
- text: normalized.caption,
178
- mediaUrl: normalized.mediaUrl,
179
- mediaUrls: normalized.mediaUrls,
180
- asVoice: normalized.asVoice,
181
- audioAsVoice: normalized.audioAsVoice,
182
- type: normalized.type,
183
- downloadMedia: normalized.downloadMedia,
184
- extra: normalized.extra,
185
- mediaLocalRoots,
186
- resolveVerifiedTarget: (to, accountId) =>
187
- toolActionBridge.resolveVerifiedTarget(to, accountId),
188
- rememberSessionRoute: (sessionKey, accountId, route) =>
189
- toolActionBridge.rememberSessionRoute(sessionKey, accountId, route),
190
- enqueueFromReply: (args) => toolActionBridge.enqueueFromReply(args),
191
- createMessageId: () => randomUUID(),
192
- })
193
- : await sendBncrText({
194
- channelId: runtime.channelId,
195
- accountId: normalized.accountId,
196
- to: normalized.to,
197
- text: normalized.message,
198
- extra: normalized.extra,
199
- mediaLocalRoots,
200
- resolveVerifiedTarget: (to, accountId) =>
201
- toolActionBridge.resolveVerifiedTarget(to, accountId),
202
- rememberSessionRoute: (sessionKey, accountId, route) =>
203
- toolActionBridge.rememberSessionRoute(sessionKey, accountId, route),
204
- enqueueFromReply: (args) => toolActionBridge.enqueueFromReply(args),
205
- createMessageId: () => randomUUID(),
206
- });
191
+ /* -- bridge to sendDispatch (normalization + routing inside) ----- */
192
+ const outboundBridge = runtime.getOutboundBridge();
193
+ const result = await outboundBridge.channelSendText(ctx);
207
194
 
208
195
  return runtime.openClawJsonResult({ ok: true, ...result });
209
196
  },
@@ -1,18 +1,8 @@
1
1
  import type { GatewayRequestHandlerOptions } from 'openclaw/plugin-sdk/core';
2
- import type { RegisterDriftSnapshot } from '../core/register-trace.ts';
3
- import type { normalizeStoredSessionKey as normalizeStoredSessionKeyFromRuntime } from '../core/targets.ts';
4
- import type {
5
- BncrConnection,
6
- BncrRoute,
7
- FileSendTransferState,
8
- OutboxEntry,
9
- } from '../core/types.ts';
2
+ import type { BncrRoute, OutboxEntry } from '../core/types.ts';
10
3
  import type { BncrGroupHistoryMap } from '../messaging/inbound/group-history.ts';
11
4
  import type { parseBncrInboundParams } from '../messaging/inbound/parse.ts';
12
- import type {
13
- NormalizedReplyPayload,
14
- ReplyPayloadInput,
15
- } from '../messaging/outbound/reply-enqueue.ts';
5
+ import type { ReplyPayloadInput } from '../messaging/outbound/reply-enqueue.ts';
16
6
  import type { OutboundReplyTargetPolicy } from '../messaging/outbound/reply-target-policy.ts';
17
7
  import type { createBncrInboundHandlersComponent } from './channel-components.ts';
18
8
  import type {
@@ -26,14 +16,7 @@ import type {
26
16
  } from './channel-runtime-types.ts';
27
17
  import type { LeaseEventPayload } from './connection-handlers.ts';
28
18
  import type { BncrActiveConnectionDebugEntry } from './connection-state.ts';
29
- import type { FileAckPayloadState, FileAckWaiter } from './file-ack-runtime.ts';
30
19
  import type { createBncrFileInboundHandlers } from './file-inbound-handlers.ts';
31
- import type { BncrFileTransferOrchestratorRuntime } from './file-transfer-orchestrator.ts';
32
- import type { BncrFileTransferRouteDiagnostics } from './file-transfer-setup.ts';
33
- import type { MessageAckWaiter } from './message-ack-runtime.ts';
34
-
35
- type StoredRouteRecord = { accountId: string; route: BncrRoute; updatedAt: number };
36
- type StoredLastSessionRecord = { sessionKey: string; scope: string; updatedAt: number };
37
20
 
38
21
  // Delivery-side wiring catalog.
39
22
  //
@@ -42,349 +25,6 @@ type StoredLastSessionRecord = { sessionKey: string; scope: string; updatedAt: n
42
25
  // 2) outbound + file-transfer state machines
43
26
  // 3) inbound/public send surfaces
44
27
 
45
- export function buildBncrConnectionStateRuntime(deps: {
46
- bridgeId: string;
47
- now: () => number;
48
- asString: (value: unknown, fallback?: string) => string;
49
- connectTtlMs: number;
50
- recentInboundSendWindowMs: number;
51
- outboundReadyTtlMs: number;
52
- preferredOutboundTtlMs: number;
53
- connections: Map<string, BncrConnection>;
54
- activeConnectionByAccount: Map<string, string>;
55
- lastInboundByAccount: Map<string, number>;
56
- lastActivityByAccount: Map<string, number>;
57
- gcTransientState: () => void;
58
- connectionKey: (accountId: string, clientId?: string) => string;
59
- buildActiveConnectionDebugList: (
60
- accountId: string,
61
- options?: { includeOutboundState?: boolean },
62
- ) => BncrActiveConnectionDebugEntry[];
63
- rememberGatewayContext: (context: GatewayRequestHandlerOptions['context']) => void;
64
- markActivity: (accountId: string, at?: number) => void;
65
- logInfo: (scope: string, message: string, options?: { debugOnly?: boolean }) => void;
66
- logInfoDedupJson: (
67
- scope: string,
68
- label: string,
69
- payload: unknown,
70
- options?: { key?: string; sig?: string; debugOnly?: boolean },
71
- ) => void;
72
- }) {
73
- return { ...deps };
74
- }
75
-
76
- export function buildBncrFileTransferRuntime(deps: {
77
- bridgeId: string;
78
- now: () => number;
79
- asString: (value: unknown, fallback?: string) => string;
80
- clampFiniteNumber: (value: unknown, fallback: number, min?: number, max?: number) => number;
81
- fileAckTimeoutMs: number;
82
- maxEarlyFileAcks: number;
83
- fileAckWaiters: Map<string, FileAckWaiter>;
84
- earlyFileAcks: Map<string, FileAckPayloadState>;
85
- getFileAckOwnerInfo: (transferId: string) => Record<string, unknown>;
86
- fileForceChunk: boolean;
87
- fileInlineThreshold: number;
88
- normalizeAccountId: (accountId: string) => string;
89
- loadOutboundTransferMedia: (args: {
90
- mediaUrl: string;
91
- mediaLocalRoots?: readonly string[];
92
- }) => Promise<{ loaded: { buffer: Buffer }; size: number; mimeType?: string; fileName: string }>;
93
- resolveOutboxPushOwner: (accountId: string) => { connId?: string; clientId?: string } | null;
94
- hasRecentInboundReachability: (accountId: string) => boolean;
95
- buildTransferRouteDiagnostics: (args: {
96
- accountId: string;
97
- recentInboundReachable: boolean;
98
- }) => BncrFileTransferRouteDiagnostics;
99
- selectTransferConnIds: (args: {
100
- directConnIds: Set<string>;
101
- recentConnIds: Set<string>;
102
- recentInboundReachable: boolean;
103
- }) => Set<string>;
104
- broadcastToConnIds: (event: string, payload: unknown, connIds: ReadonlySet<string>) => void;
105
- chunkEvent: string;
106
- completeEvent: string;
107
- logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
108
- logWarn: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
109
- }) {
110
- return { ...deps };
111
- }
112
-
113
- export function buildBncrStateTransientRuntime(deps: {
114
- bridgeId: string;
115
- getStatePath: () => string | null;
116
- now: () => number;
117
- asString: (value: unknown, fallback?: string) => string;
118
- finiteNumberOr: (value: unknown, fallback: number) => number;
119
- normalizeAccountId: (accountId: string) => string;
120
- normalizeStoredSessionKey: typeof normalizeStoredSessionKeyFromRuntime;
121
- parseRouteLike: (value: unknown) => BncrRoute | null;
122
- routeKey: (accountId: string, route: BncrRoute) => string;
123
- formatDisplayScope: (route: BncrRoute) => string;
124
- canonicalAgentId: () => string;
125
- normalizePersistedOutboxEntry: (entry: unknown) => OutboxEntry | null;
126
- maxDeadLetterEntries: number;
127
- maxSessionRouteEntries: number;
128
- maxAccountActivityEntries: number;
129
- sceneRegistry: Map<string, BncrSceneRecord>;
130
- groupHistories: Map<
131
- string,
132
- import('./channel-runtime-types.ts').BncrPersistedGroupHistoryEntry[]
133
- >;
134
- outbox: Map<string, OutboxEntry>;
135
- getDeadLetter: () => OutboxEntry[];
136
- setDeadLetter: (entries: OutboxEntry[]) => void;
137
- sessionRoutes: Map<string, StoredRouteRecord>;
138
- routeAliases: Map<string, StoredRouteRecord>;
139
- lastSessionByAccount: Map<string, StoredLastSessionRecord>;
140
- lastActivityByAccount: Map<string, number>;
141
- lastInboundByAccount: Map<string, number>;
142
- lastOutboundByAccount: Map<string, number>;
143
- getLastDriftSnapshot: () => RegisterDriftSnapshot | null;
144
- setLastDriftSnapshot: (value: RegisterDriftSnapshot | null) => void;
145
- connectTtlMs: number;
146
- fileTransferKeepMs: number;
147
- fileTransferTerminalKeepMs: number;
148
- fileTransferAckTtlMs: number;
149
- connections: Map<string, BncrConnection>;
150
- activeConnectionByAccount: Map<string, string>;
151
- recentInbound: Map<string, number>;
152
- fileSendTransfers: Map<string, { status: string; startedAt: number; terminalAt?: number }>;
153
- fileRecvTransfers: Map<string, { status: string; startedAt: number; terminalAt?: number }>;
154
- earlyFileAcks: Map<string, { at: number }>;
155
- logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
156
- }) {
157
- return { ...deps };
158
- }
159
-
160
- export function buildBncrAckOutboxRuntime(deps: {
161
- bridgeId: string;
162
- pushEvent: string;
163
- now: () => number;
164
- asString: (value: unknown, fallback?: string) => string;
165
- backoffMs: (retryCount: number) => number;
166
- isPlainObject: (value: unknown) => value is Record<string, unknown>;
167
- clampFiniteNumber: (value: unknown, fallback: number, min?: number, max?: number) => number;
168
- normalizeAccountId: (accountId: string) => string;
169
- formatDisplayScope: (route: BncrRoute) => string;
170
- isFileTransferEntry: (entry: OutboxEntry) => boolean;
171
- recommendedAckTimeoutMaxMs: number;
172
- adaptiveAckTimeoutEnabled: boolean;
173
- defaultAckTimeoutMs: number;
174
- stopped: () => boolean;
175
- outbox: Map<string, OutboxEntry>;
176
- deadLetter: () => OutboxEntry[];
177
- connectionsValues: () => IterableIterator<BncrConnection>;
178
- gatewayContextAvailable: () => boolean;
179
- messageAckWaiters: Map<string, MessageAckWaiter>;
180
- fileAckWaiterCount: () => number;
181
- activeConnectionCount: (accountId: string) => number;
182
- getAccountPendingOutboxEntries: (accountId: string) => OutboxEntry[];
183
- pushDrainRunningAccounts: Set<string>;
184
- pushDrainRunningSinceByAccount: Map<string, number>;
185
- pushDrainStuckWarnedAtByAccount: Map<string, number>;
186
- isOnline: (accountId: string) => boolean;
187
- hasRecentInboundReachability: (accountId: string) => boolean;
188
- isOutboundAckRequired: (accountId?: string) => boolean;
189
- resolveMessageAckTimeoutMs: (accountId: string) => number;
190
- waitForMessageAck: (messageId: string, waitMs: number) => Promise<'acked' | 'timeout'>;
191
- resolvePushConnIds: (accountId: string) => Iterable<string>;
192
- sleepMs: (ms: number) => Promise<void>;
193
- schedulePushDrain: (delayMs: number) => void;
194
- tryPushEntry: (entry: OutboxEntry) => Promise<boolean>;
195
- handleFileTransferPushFailure: (args: { entry: OutboxEntry; error: unknown }) => void;
196
- handleTextPushFailure: (args: { entry: OutboxEntry; error: unknown }) => void;
197
- isPrePushGuardDeferral: (entry: OutboxEntry) => boolean;
198
- scheduleSave: () => void;
199
- logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
200
- logWarn: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
201
- logError: (scope: string, message: string) => void;
202
- observeLease: (
203
- kind:
204
- | 'connect'
205
- | 'inbound'
206
- | 'activity'
207
- | 'ack'
208
- | 'file.init'
209
- | 'file.chunk'
210
- | 'file.complete'
211
- | 'file.abort',
212
- payload: Record<string, unknown>,
213
- ) => { stale: boolean };
214
- rememberGatewayContext: (context: GatewayRequestHandlerOptions['context']) => void;
215
- markSeen: (accountId: string, connId: string, clientId?: string) => void;
216
- markOutboundCapability: (args: {
217
- accountId: string;
218
- connId: string;
219
- clientId?: string;
220
- outboundReady: boolean;
221
- preferredForOutbound: boolean;
222
- }) => void;
223
- recordAckOkTelemetry: (args: {
224
- accountId: string;
225
- entry: OutboxEntry;
226
- telemetryPatch: { queueLatencyMs?: number; pushLatencyMs?: number; lateAfterTimeout?: boolean };
227
- }) => void;
228
- deleteOutboxEntry: (messageId: string) => void;
229
- setOutboxEntry: (messageId: string, entry: OutboxEntry) => void;
230
- resolveMessageAck: (messageId: string, result: 'acked' | 'timeout') => boolean;
231
- moveToDeadLetter: (entry: OutboxEntry, reason: string) => void;
232
- recordAckTimeoutTelemetry: (accountId: string) => void;
233
- degradeOutboundCapability: (args: {
234
- accountId: string;
235
- connId?: string;
236
- clientId?: string;
237
- reason: string;
238
- }) => void;
239
- flushPushQueueBestEffort: (args?: {
240
- accountId?: string;
241
- trigger?: string;
242
- reason?: string;
243
- }) => void;
244
- flushTriggerTimer: string;
245
- flushReasonScheduledDrain: string;
246
- outboundFlushTriggerAckOk: string;
247
- outboundFlushReasonMessageAcked: string;
248
- pushDrainExceptionRetryLimit: number;
249
- pushDrainExceptionRetryDelayMs: number;
250
- pushDrainStuckWarnMs: number;
251
- pushDrainIntervalMs: number;
252
- pushDrainAccountTimeBudgetMs: number;
253
- pushDrainAccountBudget: number;
254
- pushAckTimeoutMs: number;
255
- maxRetry: number;
256
- prePushGuardRetryDelayMs: number;
257
- }) {
258
- return { ...deps };
259
- }
260
-
261
- export function buildBncrOutboxPushRouteRuntime(deps: {
262
- bridgeId: string;
263
- pushEvent: string;
264
- now: () => number;
265
- connectTtlMs: number;
266
- finiteNumberOr: (value: unknown, fallback: number) => number;
267
- outboxSize: () => number;
268
- gatewayBroadcastToConnIds: (
269
- event: string,
270
- payload: unknown,
271
- connIds: ReadonlySet<string>,
272
- ) => void;
273
- recordOutboxPushSuccess: (args: {
274
- entry: OutboxEntry;
275
- connIds: Iterable<string>;
276
- ownerConnId?: string;
277
- ownerClientId?: string;
278
- clearLastError?: boolean;
279
- }) => void;
280
- recordOutboxPushFailure: (args: {
281
- entry: OutboxEntry;
282
- error: unknown;
283
- fallbackError: string;
284
- persist?: boolean;
285
- }) => void;
286
- recordOutboxPrePushFailure: (args: {
287
- entry: OutboxEntry;
288
- lastError: string;
289
- persist?: boolean;
290
- }) => void;
291
- recordPrePushGuardSkip: (args: { accountId: string; reason: string }) => void;
292
- moveToDeadLetter: (entry: OutboxEntry, reason: string) => void;
293
- activeConnectionCount: (accountId: string) => number;
294
- connections: Map<string, BncrConnection>;
295
- connectionsValues: () => Iterable<BncrConnection>;
296
- activeConnectionByAccount: Map<string, string>;
297
- resolveRecentInboundConnIds: (accountId: string) => Set<string>;
298
- connectionKey: (accountId: string, clientId?: string) => string;
299
- isRetryableFileTransferError: (value: unknown) => boolean;
300
- logInfo: (scope: string, message: string, options?: { debugOnly?: boolean }) => void;
301
- buildActiveConnectionDebugList: (
302
- accountId: string,
303
- options?: { includeOutboundState?: boolean },
304
- ) => BncrActiveConnectionDebugEntry[];
305
- }) {
306
- return { ...deps };
307
- }
308
-
309
- export function buildBncrMediaOrchestratorsRuntime(deps: {
310
- now: () => number;
311
- asString: (value: unknown, fallback?: string) => string;
312
- fileSendTransfers: Map<string, FileSendTransferState>;
313
- getGatewayContext: () => GatewayRequestHandlerOptions['context'] | null;
314
- fileInitEvent: string;
315
- fileAbortEvent: string;
316
- prepareOutboundTransfer: BncrFileTransferOrchestratorRuntime['prepareOutboundTransfer'];
317
- sendChunk: BncrFileTransferOrchestratorRuntime['sendChunk'];
318
- sendComplete: BncrFileTransferOrchestratorRuntime['sendComplete'];
319
- waitForFileAck: BncrFileTransferOrchestratorRuntime['waitForFileAck'];
320
- logFileTransferChunkAck: BncrFileTransferOrchestratorRuntime['logFileTransferChunkAck'];
321
- logFileTransferChunkAckFail: BncrFileTransferOrchestratorRuntime['logFileTransferChunkAckFail'];
322
- logFileTransferCompleteAck: BncrFileTransferOrchestratorRuntime['logFileTransferCompleteAck'];
323
- logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
324
- logEnqueueFromReply: (args: {
325
- accountId: string;
326
- sessionKey: string;
327
- route: BncrRoute;
328
- payload: NormalizedReplyPayload;
329
- }) => void;
330
- enqueueOutbound: (entry: OutboxEntry) => void;
331
- buildTextOutboxEntry: (args: {
332
- accountId: string;
333
- sessionKey: string;
334
- route: BncrRoute;
335
- text: string;
336
- extra?: Record<string, unknown>;
337
- kind?: 'tool' | 'block' | 'final';
338
- replyToId?: string;
339
- replyTargetPolicy?: OutboundReplyTargetPolicy;
340
- }) => OutboxEntry;
341
- buildFileTransferOutboxEntry: (args: {
342
- accountId: string;
343
- sessionKey: string;
344
- route: BncrRoute;
345
- mediaUrl: string;
346
- mediaLocalRoots?: readonly string[];
347
- text: string;
348
- asVoice: boolean;
349
- audioAsVoice: boolean;
350
- type?: string;
351
- extra?: Record<string, unknown>;
352
- kind?: 'tool' | 'block' | 'final';
353
- replyToId?: string;
354
- replyTargetPolicy?: OutboundReplyTargetPolicy;
355
- downloadMedia?: boolean;
356
- }) => OutboxEntry;
357
- rememberRecentMediaSend: (args: {
358
- sessionKey: string;
359
- mediaUrl: string;
360
- text: string;
361
- replyToId: string;
362
- createdAt: number;
363
- }) => void;
364
- tryBuildMediaDedupeFallback: (args: {
365
- sessionKey: string;
366
- mediaUrl: string;
367
- text: string;
368
- replyToId: string;
369
- currentTime: number;
370
- }) => {
371
- text: string;
372
- reason: 'same-text-sent-checkmark' | 'text-changed-downgrade';
373
- } | null;
374
- }) {
375
- return {
376
- ...deps,
377
- getGatewayContext: () => {
378
- const context = deps.getGatewayContext();
379
- if (!context) return null;
380
- return {
381
- broadcastToConnIds: (event: string, payload: unknown, connIds: ReadonlySet<string>) =>
382
- context.broadcastToConnIds(event, payload, connIds),
383
- };
384
- },
385
- };
386
- }
387
-
388
28
  export function buildBncrInboundSurfaceRuntime(deps: {
389
29
  getApi: Parameters<typeof createBncrInboundHandlersComponent>[0]['getApi'];
390
30
  channelId: string;
@@ -535,6 +175,7 @@ export function buildBncrChannelSendRuntime(deps: {
535
175
  replyTargetPolicy?: OutboundReplyTargetPolicy;
536
176
  }) => Promise<void>;
537
177
  listOutboxEntries: () => OutboxEntry[];
178
+ resolveSceneDownloadMedia?: (to: string) => boolean | undefined;
538
179
  }) {
539
180
  return { ...deps };
540
181
  }