@xmoxmo/bncr 0.5.6 → 0.5.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.
Files changed (73) 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 -6
  5. package/scripts/selfcheck.mjs +0 -7
  6. package/src/channel.ts +79 -43
  7. package/src/core/accounts.ts +1 -6
  8. package/src/core/connection-reachability.ts +1 -5
  9. package/src/core/dead-letter-diagnostics.ts +4 -10
  10. package/src/core/outbox-entry-builders.ts +34 -18
  11. package/src/core/outbox-file-transfer-failure.ts +5 -0
  12. package/src/core/outbox-file-transfer-guards.ts +6 -0
  13. package/src/core/outbox-file-transfer-prep.ts +8 -5
  14. package/src/core/outbox-file-transfer-success.ts +5 -0
  15. package/src/core/outbox-queue.ts +1 -7
  16. package/src/core/outbox-summary.ts +15 -27
  17. package/src/core/outbox-text-push-prep.ts +1 -36
  18. package/src/core/permissions.ts +1 -9
  19. package/src/core/persisted-outbox-entry.ts +54 -11
  20. package/src/core/policy.ts +1 -11
  21. package/src/core/register-trace.ts +2 -5
  22. package/src/core/status-meta.ts +1 -9
  23. package/src/core/status.ts +1 -21
  24. package/src/core/targets.ts +52 -70
  25. package/src/core/types.ts +1 -17
  26. package/src/core/value-sanitize.ts +38 -1
  27. package/src/messaging/inbound/commands.ts +2 -24
  28. package/src/messaging/inbound/dispatch-prep.ts +1 -1
  29. package/src/messaging/inbound/gate.ts +2 -31
  30. package/src/messaging/inbound/group-history.ts +0 -75
  31. package/src/messaging/inbound/parse.ts +1 -31
  32. package/src/messaging/inbound/scene-admin.ts +8 -8
  33. package/src/messaging/outbound/diagnostics.ts +1 -6
  34. package/src/messaging/outbound/durable-queue-adapter.ts +17 -11
  35. package/src/messaging/outbound/marker-parser.ts +62 -28
  36. package/src/messaging/outbound/media-dedupe.ts +2 -6
  37. package/src/messaging/outbound/media.ts +1 -6
  38. package/src/messaging/outbound/normalize-outbound-send.ts +327 -0
  39. package/src/messaging/outbound/queue-selectors.ts +1 -5
  40. package/src/messaging/outbound/reply-enqueue.ts +121 -70
  41. package/src/messaging/outbound/session-route.ts +6 -11
  42. package/src/messaging/outbound/target-resolver.ts +1 -6
  43. package/src/openclaw/routing-runtime.ts +0 -11
  44. package/src/openclaw/sdk-helpers.ts +0 -2
  45. package/src/plugin/bridge-file-transfer-push-facade.ts +6 -6
  46. package/src/plugin/bridge-media-facade.ts +0 -1
  47. package/src/plugin/channel-components.ts +0 -13
  48. package/src/plugin/channel-plugin-surface-group.ts +52 -65
  49. package/src/plugin/channel-runtime-builders-delivery.ts +1 -0
  50. package/src/plugin/channel-runtime-types.ts +2 -24
  51. package/src/plugin/channel-send.ts +205 -221
  52. package/src/plugin/channel-utils.ts +8 -29
  53. package/src/plugin/connection-handlers-helpers.ts +0 -15
  54. package/src/plugin/connection-handlers.ts +6 -5
  55. package/src/plugin/file-inbound-abort.ts +5 -0
  56. package/src/plugin/file-inbound-chunk.ts +5 -0
  57. package/src/plugin/file-inbound-complete.ts +5 -0
  58. package/src/plugin/media-dedupe-runtime.ts +1 -6
  59. package/src/plugin/message-send.ts +7 -8
  60. package/src/plugin/outbound.ts +6 -26
  61. package/src/plugin/outbox-drain-loop.ts +2 -2
  62. package/src/plugin/outbox-file-push-flow.ts +7 -7
  63. package/src/plugin/outbox-text-push-flow.ts +1 -1
  64. package/src/plugin/target-runtime.ts +1 -6
  65. package/src/runtime/outbound-ack-timeout.ts +1 -8
  66. package/src/runtime/outbox-transitions.ts +1 -4
  67. package/src/runtime/status-worker.ts +1 -9
  68. package/src/messaging/outbound/build-send-action.ts +0 -130
  69. package/src/messaging/outbound/durable-message-adapter.ts +0 -121
  70. package/src/messaging/outbound/send-params.ts +0 -114
  71. package/src/messaging/outbound/send.ts +0 -127
  72. package/src/openclaw/ingress-runtime.ts +0 -25
  73. package/src/plugin/channel-send-runtime-group.ts +0 -37
@@ -4,6 +4,7 @@ import {
4
4
  parseStrictBncrSessionKey,
5
5
  } from '../../core/targets.ts';
6
6
  import type { BncrRoute } from '../../core/types.ts';
7
+ import { asString } from '../../core/value-sanitize.ts';
7
8
 
8
9
  type ResolveBncrOutboundTargetParams = {
9
10
  target: string;
@@ -11,12 +12,6 @@ type ResolveBncrOutboundTargetParams = {
11
12
  resolveRouteBySession?: (raw: string, accountId: string) => BncrRoute | null;
12
13
  };
13
14
 
14
- function asString(v: unknown, fallback = ''): string {
15
- if (typeof v === 'string') return v;
16
- if (v == null) return fallback;
17
- return String(v);
18
- }
19
-
20
15
  export function looksLikeBncrExplicitTarget(input: string): boolean {
21
16
  const raw = asString(input).trim();
22
17
  if (!raw) return false;
@@ -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;
@@ -29,7 +29,7 @@ export function createBncrBridgeFileTransferPushFacade(runtime: {
29
29
  }>;
30
30
  buildFileTransferOutboundFrame: (params: {
31
31
  entry: OutboxEntry;
32
- meta: Record<string, unknown>;
32
+ msg: Record<string, unknown>;
33
33
  media: { fileName?: string; mimeType?: string; path?: string; base64?: string; type?: string };
34
34
  mediaUrl: string;
35
35
  }) => Record<string, unknown>;
@@ -74,7 +74,7 @@ export function createBncrBridgeFileTransferPushFacade(runtime: {
74
74
  }) {
75
75
  const pushFileTransferSuccessPath = async (args: {
76
76
  entry: OutboxEntry;
77
- meta: Record<string, unknown>;
77
+ msg: Record<string, unknown>;
78
78
  owner: BncrConnection | null;
79
79
  connIds: Iterable<string>;
80
80
  recentInboundReachable: boolean;
@@ -86,14 +86,14 @@ export function createBncrBridgeFileTransferPushFacade(runtime: {
86
86
  sessionKey: args.entry.sessionKey,
87
87
  route: args.entry.route,
88
88
  mediaUrl: args.mediaUrl,
89
- mediaLocalRoots: Array.isArray(args.meta.mediaLocalRoots)
90
- ? args.meta.mediaLocalRoots.filter((v): v is string => typeof v === 'string')
89
+ mediaLocalRoots: Array.isArray(args.msg.mediaLocalRoots)
90
+ ? args.msg.mediaLocalRoots.filter((v): v is string => typeof v === 'string')
91
91
  : undefined,
92
- downloadMedia: args.meta.downloadMedia === true,
92
+ downloadMedia: args.msg.downloadMedia as boolean | undefined,
93
93
  });
94
94
  const frame = runtime.buildFileTransferOutboundFrame({
95
95
  entry: args.entry,
96
- meta: args.meta,
96
+ msg: args.msg,
97
97
  media,
98
98
  mediaUrl: args.mediaUrl,
99
99
  });
@@ -161,7 +161,6 @@ export function createBncrBridgeMediaFacade(runtime: {
161
161
  const loadOutboundTransferMedia = async (params: {
162
162
  mediaUrl: string;
163
163
  mediaLocalRoots?: readonly string[];
164
- downloadMedia?: boolean;
165
164
  }): Promise<{
166
165
  loaded: OpenClawLoadedMedia;
167
166
  size: number;
@@ -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
  },
@@ -535,6 +535,7 @@ export function buildBncrChannelSendRuntime(deps: {
535
535
  replyTargetPolicy?: OutboundReplyTargetPolicy;
536
536
  }) => Promise<void>;
537
537
  listOutboxEntries: () => OutboxEntry[];
538
+ resolveSceneDownloadMedia?: (to: string) => boolean | undefined;
538
539
  }) {
539
540
  return { ...deps };
540
541
  }
@@ -1,4 +1,4 @@
1
- import type { RegisterDriftSnapshot, RegisterTraceEntry } from '../core/register-trace.ts';
1
+ import type { RegisterDriftSnapshot } from '../core/register-trace.ts';
2
2
  import type { BncrDiagnosticsSummary, BncrRoute, OutboxEntry } from '../core/types.ts';
3
3
  import type { OpenClawChannelToolSend, openClawJsonResult } from '../openclaw/sdk-helpers.ts';
4
4
 
@@ -133,7 +133,7 @@ export type BncrChannelSendContext = {
133
133
  payload?: Record<string, unknown>;
134
134
  sessionKey?: string;
135
135
  mirror?: { sessionKey?: string };
136
- threadId?: string;
136
+ threadId?: string | number | null;
137
137
  /** Pass-through fields from host - bncr plugin does NOT consume these. */
138
138
  forceDocument?: boolean;
139
139
  gifPlayback?: boolean;
@@ -143,28 +143,6 @@ export type BncrChannelSendContext = {
143
143
  extra?: Record<string, unknown>;
144
144
  };
145
145
 
146
- export type BncrRegisterRuntimeSnapshot = {
147
- bridgeId: string;
148
- gatewayPid: number;
149
- pluginVersion: string | null;
150
- pluginSource: string | null;
151
- lastApiInstanceId: string | null;
152
- lastRegistryFingerprint: string | null;
153
- registerCount: number;
154
- firstRegisterAt: number | null;
155
- lastRegisterAt: number | null;
156
- lastApiRebindAt: number | null;
157
- apiGeneration: number;
158
- registerTraceRecent: RegisterTraceEntry[];
159
- lastDriftSnapshot: RegisterDriftSnapshot | null;
160
- };
161
-
162
- export type FileAckPayloadState = {
163
- payload: Record<string, unknown>;
164
- ok: boolean;
165
- at: number;
166
- };
167
-
168
146
  export type ChannelMessageActionAdapter = {
169
147
  describeMessageTool: (ctx: { cfg: BncrChannelConfigRoot }) => {
170
148
  actions: readonly ['send'];