@xmoxmo/bncr 0.5.5 → 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 (78) 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 +114 -37
  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 +38 -15
  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/native-command.ts +7 -3
  32. package/src/messaging/inbound/parse.ts +1 -31
  33. package/src/messaging/inbound/scene-admin.ts +181 -5
  34. package/src/messaging/outbound/diagnostics.ts +1 -6
  35. package/src/messaging/outbound/durable-queue-adapter.ts +17 -11
  36. package/src/messaging/outbound/marker-parser.ts +188 -0
  37. package/src/messaging/outbound/media-dedupe.ts +2 -6
  38. package/src/messaging/outbound/media.ts +6 -20
  39. package/src/messaging/outbound/normalize-outbound-send.ts +327 -0
  40. package/src/messaging/outbound/queue-selectors.ts +1 -5
  41. package/src/messaging/outbound/reply-enqueue-media.ts +4 -0
  42. package/src/messaging/outbound/reply-enqueue.ts +130 -57
  43. package/src/messaging/outbound/session-route.ts +6 -11
  44. package/src/messaging/outbound/target-resolver.ts +1 -6
  45. package/src/openclaw/routing-runtime.ts +0 -11
  46. package/src/openclaw/sdk-helpers.ts +0 -2
  47. package/src/plugin/bridge-file-transfer-push-facade.ts +8 -6
  48. package/src/plugin/bridge-media-facade.ts +1 -1
  49. package/src/plugin/channel-components.ts +0 -13
  50. package/src/plugin/channel-plugin-surface-group.ts +57 -40
  51. package/src/plugin/channel-runtime-builders-delivery.ts +3 -0
  52. package/src/plugin/channel-runtime-types.ts +10 -24
  53. package/src/plugin/channel-send.ts +241 -154
  54. package/src/plugin/channel-utils.ts +8 -29
  55. package/src/plugin/connection-handlers-helpers.ts +0 -15
  56. package/src/plugin/connection-handlers.ts +6 -5
  57. package/src/plugin/file-inbound-abort.ts +5 -0
  58. package/src/plugin/file-inbound-chunk.ts +5 -0
  59. package/src/plugin/file-inbound-complete.ts +5 -0
  60. package/src/plugin/file-transfer-orchestrator.ts +105 -3
  61. package/src/plugin/file-transfer-setup.ts +1 -1
  62. package/src/plugin/media-dedupe-runtime.ts +1 -6
  63. package/src/plugin/media-orchestrators-runtime-group.ts +2 -0
  64. package/src/plugin/message-send.ts +7 -8
  65. package/src/plugin/outbound.ts +6 -22
  66. package/src/plugin/outbox-drain-loop.ts +2 -2
  67. package/src/plugin/outbox-file-push-flow.ts +7 -7
  68. package/src/plugin/outbox-text-push-flow.ts +1 -1
  69. package/src/plugin/target-runtime.ts +1 -6
  70. package/src/runtime/outbound-ack-timeout.ts +1 -8
  71. package/src/runtime/outbox-transitions.ts +1 -4
  72. package/src/runtime/status-worker.ts +1 -9
  73. package/src/messaging/outbound/build-send-action.ts +0 -130
  74. package/src/messaging/outbound/durable-message-adapter.ts +0 -121
  75. package/src/messaging/outbound/send-params.ts +0 -81
  76. package/src/messaging/outbound/send.ts +0 -121
  77. package/src/openclaw/ingress-runtime.ts +0 -25
  78. package/src/plugin/channel-send-runtime-group.ts +0 -37
@@ -1,121 +0,0 @@
1
- import type {
2
- ChannelMessageAdapterShape,
3
- ChannelMessageSendMediaContext,
4
- ChannelMessageSendPayloadContext,
5
- ChannelMessageSendResult,
6
- ChannelMessageSendTextContext,
7
- } from 'openclaw/plugin-sdk/channel-message';
8
- import { defineChannelMessageAdapter } from 'openclaw/plugin-sdk/channel-message';
9
- import type { OpenClawConfig } from 'openclaw/plugin-sdk/core';
10
-
11
- import {
12
- buildFileTransferOutboxEntry,
13
- buildTextOutboxEntry,
14
- } from '../../core/outbox-entry-builders.ts';
15
- import type { BncrRoute, OutboxEntry } from '../../core/types.ts';
16
- import { buildBncrDurableQueuedResult } from './durable-queue-adapter.ts';
17
-
18
- export type BncrDurableMessageQueuedAdapterDeps = {
19
- enqueueText: (
20
- ctx: ChannelMessageSendTextContext<OpenClawConfig>,
21
- ) => Promise<OutboxEntry> | OutboxEntry;
22
- enqueueMedia?: (
23
- ctx: ChannelMessageSendMediaContext<OpenClawConfig>,
24
- ) => Promise<OutboxEntry> | OutboxEntry;
25
- enqueuePayload?: (
26
- ctx: ChannelMessageSendPayloadContext<OpenClawConfig>,
27
- ) => Promise<OutboxEntry> | OutboxEntry;
28
- now?: () => number;
29
- };
30
-
31
- export type BncrDurableMessageQueuedAdapterBuilderDeps = {
32
- createMessageId: () => string;
33
- now: () => number;
34
- normalizeAccountId: (accountId?: string | null) => string;
35
- normalizeReplyToId: (value?: string | null) => string;
36
- resolveTarget: (ctx: ChannelMessageSendTextContext<OpenClawConfig>) => {
37
- route: BncrRoute;
38
- sessionKey: string;
39
- accountId?: string | null;
40
- };
41
- filePushEvent?: string;
42
- };
43
-
44
- // This adapter intentionally models only the OpenClaw -> bncr-plugin handoff.
45
- // Once a message is accepted into bncr's own outbox, OpenClaw should stop managing it;
46
- // client/platform ACK, retry, and deadLetter remain owned by the bncr service framework.
47
- export function createBncrDurableMessageQueuedAdapter(
48
- deps: BncrDurableMessageQueuedAdapterDeps,
49
- ): ChannelMessageAdapterShape<OpenClawConfig, ChannelMessageSendResult> {
50
- return defineChannelMessageAdapter({
51
- id: 'bncr-queued-outbox',
52
- receive: {
53
- defaultAckPolicy: 'manual',
54
- supportedAckPolicies: ['manual'],
55
- },
56
- send: {
57
- text: async (ctx) => toChannelMessageSendResult(await deps.enqueueText(ctx), deps.now),
58
- media: deps.enqueueMedia
59
- ? async (ctx) => toChannelMessageSendResult(await deps.enqueueMedia?.(ctx), deps.now)
60
- : undefined,
61
- payload: deps.enqueuePayload
62
- ? async (ctx) => toChannelMessageSendResult(await deps.enqueuePayload?.(ctx), deps.now)
63
- : undefined,
64
- },
65
- });
66
- }
67
-
68
- export function createBncrDurableMessageQueuedAdapterFromBuilders(
69
- deps: BncrDurableMessageQueuedAdapterBuilderDeps,
70
- ): ChannelMessageAdapterShape<OpenClawConfig, ChannelMessageSendResult> {
71
- return createBncrDurableMessageQueuedAdapter({
72
- now: deps.now,
73
- enqueueText: (ctx) => {
74
- const resolved = deps.resolveTarget(ctx);
75
- return buildTextOutboxEntry({
76
- createMessageId: deps.createMessageId,
77
- now: deps.now,
78
- normalizeAccountId: deps.normalizeAccountId,
79
- normalizeReplyToId: deps.normalizeReplyToId,
80
- accountId: resolved.accountId ?? ctx.accountId ?? '',
81
- sessionKey: resolved.sessionKey,
82
- route: resolved.route,
83
- text: ctx.text,
84
- kind: 'final',
85
- replyToId: ctx.replyToId ?? undefined,
86
- });
87
- },
88
- enqueueMedia: (ctx) => {
89
- const resolved = deps.resolveTarget(ctx);
90
- return buildFileTransferOutboxEntry({
91
- createMessageId: deps.createMessageId,
92
- now: deps.now,
93
- normalizeAccountId: deps.normalizeAccountId,
94
- pushEvent: deps.filePushEvent ?? 'bncr.file.push',
95
- accountId: resolved.accountId ?? ctx.accountId ?? '',
96
- sessionKey: resolved.sessionKey,
97
- route: resolved.route,
98
- mediaUrl: ctx.mediaUrl,
99
- mediaLocalRoots: ctx.mediaLocalRoots,
100
- text: ctx.text,
101
- asVoice: ctx.audioAsVoice,
102
- audioAsVoice: ctx.audioAsVoice,
103
- kind: 'final',
104
- replyToId: ctx.replyToId ?? undefined,
105
- });
106
- },
107
- });
108
- }
109
-
110
- function toChannelMessageSendResult(
111
- entry: OutboxEntry | undefined,
112
- now?: () => number,
113
- ): ChannelMessageSendResult {
114
- if (!entry) throw new Error('bncr durable message adapter did not receive an outbox entry');
115
- const queued = buildBncrDurableQueuedResult({ entry, sentAt: now?.() });
116
- const receipt: ChannelMessageSendResult['receipt'] = queued.receipt;
117
- return {
118
- receipt,
119
- messageId: queued.receipt.primaryPlatformMessageId,
120
- };
121
- }
@@ -1,81 +0,0 @@
1
- import { normalizeAccountId } from '../../core/accounts.ts';
2
- import { readOpenClawBooleanParam, readOpenClawStringParam } from '../../openclaw/sdk-helpers.ts';
3
-
4
- export type NormalizedBncrSendParams = {
5
- to: string;
6
- accountId: string;
7
- message: string;
8
- caption: string;
9
- mediaUrl?: string;
10
- mediaUrls?: string[];
11
- asVoice: boolean;
12
- audioAsVoice: boolean;
13
- type?: string;
14
- extra?: Record<string, unknown>;
15
- };
16
-
17
- function isPlainObject(value: unknown): value is Record<string, unknown> {
18
- return typeof value === 'object' && value !== null && !Array.isArray(value);
19
- }
20
-
21
- export function normalizeBncrSendParams(input: {
22
- params: unknown;
23
- accountId: string;
24
- }): NormalizedBncrSendParams {
25
- const paramsObj = isPlainObject(input.params) ? input.params : {};
26
- const to = readOpenClawStringParam(paramsObj, 'to', { required: true });
27
- const resolvedAccountId = normalizeAccountId(
28
- readOpenClawStringParam(paramsObj, 'accountId') ?? input.accountId,
29
- );
30
-
31
- const message = readOpenClawStringParam(paramsObj, 'message', { allowEmpty: true }) ?? '';
32
- const caption = readOpenClawStringParam(paramsObj, 'caption', { allowEmpty: true }) ?? '';
33
- const mediaUrl =
34
- readOpenClawStringParam(paramsObj, 'media', { trim: false }) ??
35
- readOpenClawStringParam(paramsObj, 'path', { trim: false }) ??
36
- readOpenClawStringParam(paramsObj, 'filePath', { trim: false }) ??
37
- readOpenClawStringParam(paramsObj, 'mediaUrl', { trim: false });
38
- const rawMediaUrls = paramsObj.mediaUrls;
39
- const mediaUrls = Array.isArray(rawMediaUrls)
40
- ? Array.from(
41
- new Set(rawMediaUrls.map((v) => (typeof v === 'string' ? v.trim() : '')).filter(Boolean)),
42
- )
43
- : undefined;
44
- // 如果 mediaUrl 已经在 mediaUrls 中,去重避免重复发送
45
- const dedupedMediaUrls = mediaUrls?.length
46
- ? mediaUrl && mediaUrls.includes(mediaUrl)
47
- ? mediaUrls
48
- : mediaUrl
49
- ? [mediaUrl, ...mediaUrls]
50
- : mediaUrls
51
- : undefined;
52
- const asVoice = readOpenClawBooleanParam(paramsObj, 'asVoice') ?? false;
53
- const audioAsVoice = readOpenClawBooleanParam(paramsObj, 'audioAsVoice') ?? false;
54
- const type = readOpenClawStringParam(paramsObj, 'type') || undefined;
55
- const rawExtra = paramsObj.extra;
56
- const extra = isPlainObject(rawExtra) ? { ...rawExtra } : undefined;
57
-
58
- const hasMedia = Boolean(mediaUrl || dedupedMediaUrls?.length);
59
-
60
- if (asVoice && !hasMedia) throw new Error('send voice requires media path');
61
-
62
- const normalizedMessage = hasMedia ? '' : message || caption || '';
63
- const normalizedCaption = hasMedia ? caption || message || '' : '';
64
-
65
- if (!normalizedMessage.trim() && !normalizedCaption.trim() && !hasMedia) {
66
- throw new Error('send requires message or media');
67
- }
68
-
69
- return {
70
- to,
71
- accountId: resolvedAccountId,
72
- message: normalizedMessage,
73
- caption: normalizedCaption,
74
- mediaUrl: dedupedMediaUrls?.length ? undefined : mediaUrl || undefined,
75
- mediaUrls: dedupedMediaUrls,
76
- asVoice,
77
- audioAsVoice,
78
- ...(type ? { type } : {}),
79
- ...(extra ? { extra } : {}),
80
- };
81
- }
@@ -1,121 +0,0 @@
1
- import type { BncrRoute } from '../../core/types.ts';
2
-
3
- function normalizeReplyKind(value: unknown): 'tool' | 'block' | 'final' | undefined {
4
- return value === 'tool' || value === 'block' || value === 'final' ? value : undefined;
5
- }
6
-
7
- export async function sendBncrText(params: {
8
- channelId: string;
9
- accountId: string;
10
- to: string;
11
- text: string;
12
- kind?: string;
13
- replyToId?: string;
14
- mediaLocalRoots?: readonly string[];
15
- resolveVerifiedTarget: (
16
- to: string,
17
- accountId: string,
18
- ) => { sessionKey: string; route: BncrRoute; displayScope: string };
19
- rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
20
- enqueueFromReply: (args: {
21
- accountId: string;
22
- sessionKey: string;
23
- route: BncrRoute;
24
- payload: {
25
- text?: string;
26
- mediaUrl?: string;
27
- mediaUrls?: string[];
28
- kind?: 'tool' | 'block' | 'final';
29
- replyToId?: string;
30
- };
31
- mediaLocalRoots?: readonly string[];
32
- }) => Promise<void>;
33
- createMessageId: () => string;
34
- }) {
35
- const verified = params.resolveVerifiedTarget(params.to, params.accountId);
36
- params.rememberSessionRoute(verified.sessionKey, params.accountId, verified.route);
37
-
38
- await params.enqueueFromReply({
39
- accountId: params.accountId,
40
- sessionKey: verified.sessionKey,
41
- route: verified.route,
42
- payload: {
43
- text: params.text,
44
- kind: normalizeReplyKind(params.kind),
45
- replyToId: params.replyToId,
46
- },
47
- mediaLocalRoots: params.mediaLocalRoots,
48
- });
49
-
50
- return {
51
- channel: params.channelId,
52
- messageId: params.createMessageId(),
53
- chatId: verified.sessionKey,
54
- };
55
- }
56
-
57
- export async function sendBncrMedia(params: {
58
- channelId: string;
59
- accountId: string;
60
- to: string;
61
- text?: string;
62
- mediaUrl?: string;
63
- mediaUrls?: string[];
64
- asVoice?: boolean;
65
- audioAsVoice?: boolean;
66
- type?: string;
67
- extra?: Record<string, unknown>;
68
- kind?: string;
69
- replyToId?: string;
70
- mediaLocalRoots?: readonly string[];
71
- resolveVerifiedTarget: (
72
- to: string,
73
- accountId: string,
74
- ) => { sessionKey: string; route: BncrRoute; displayScope: string };
75
- rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
76
- enqueueFromReply: (args: {
77
- accountId: string;
78
- sessionKey: string;
79
- route: BncrRoute;
80
- payload: {
81
- text?: string;
82
- mediaUrl?: string;
83
- mediaUrls?: string[];
84
- asVoice?: boolean;
85
- audioAsVoice?: boolean;
86
- type?: string;
87
- extra?: Record<string, unknown>;
88
- kind?: 'tool' | 'block' | 'final';
89
- replyToId?: string;
90
- };
91
- mediaLocalRoots?: readonly string[];
92
- }) => Promise<void>;
93
- createMessageId: () => string;
94
- }) {
95
- const verified = params.resolveVerifiedTarget(params.to, params.accountId);
96
- params.rememberSessionRoute(verified.sessionKey, params.accountId, verified.route);
97
-
98
- await params.enqueueFromReply({
99
- accountId: params.accountId,
100
- sessionKey: verified.sessionKey,
101
- route: verified.route,
102
- payload: {
103
- text: params.text || '',
104
- mediaUrl: params.mediaUrl || '',
105
- mediaUrls: params.mediaUrls?.length ? params.mediaUrls : undefined,
106
- asVoice: params.asVoice === true ? true : undefined,
107
- audioAsVoice: params.audioAsVoice === true ? true : undefined,
108
- type: params.type,
109
- extra: params.extra,
110
- kind: normalizeReplyKind(params.kind),
111
- replyToId: params.replyToId,
112
- },
113
- mediaLocalRoots: params.mediaLocalRoots,
114
- });
115
-
116
- return {
117
- channel: params.channelId,
118
- messageId: params.createMessageId(),
119
- chatId: verified.sessionKey,
120
- };
121
- }
@@ -1,25 +0,0 @@
1
- import {
2
- defineStableChannelIngressIdentity as sdkDefineStableChannelIngressIdentity,
3
- resolveChannelMessageIngress as sdkResolveChannelMessageIngress,
4
- } from 'openclaw/plugin-sdk/channel-ingress-runtime';
5
-
6
- type StableChannelIngressIdentityParams = Parameters<
7
- typeof sdkDefineStableChannelIngressIdentity
8
- >[0];
9
-
10
- type ResolveChannelMessageIngressParams = Parameters<typeof sdkResolveChannelMessageIngress>[0];
11
- type ResolveChannelMessageIngressResult = Awaited<
12
- ReturnType<typeof sdkResolveChannelMessageIngress>
13
- >;
14
-
15
- export function defineOpenClawStableChannelIngressIdentity(
16
- params: StableChannelIngressIdentityParams,
17
- ) {
18
- return sdkDefineStableChannelIngressIdentity(params);
19
- }
20
-
21
- export async function resolveOpenClawChannelMessageIngress(
22
- params: ResolveChannelMessageIngressParams,
23
- ): Promise<ResolveChannelMessageIngressResult> {
24
- return sdkResolveChannelMessageIngress(params);
25
- }
@@ -1,37 +0,0 @@
1
- import type { BncrRoute } from '../core/types.ts';
2
- import { createBncrChannelSendRuntimeComponent } from './channel-components.ts';
3
-
4
- export function createBncrChannelSendRuntimeGroup(runtime: {
5
- channelId: string;
6
- asString: (value: unknown, fallback?: string) => string;
7
- syncDebugFlag: () => Promise<void>;
8
- logInfo: (scope: string, message: string, options?: Record<string, unknown>) => void;
9
- resolveVerifiedTarget: (
10
- to: string,
11
- accountId: string,
12
- ) => {
13
- sessionKey: string;
14
- route: BncrRoute;
15
- displayScope: string;
16
- };
17
- rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
18
- enqueueFromReply: Parameters<typeof createBncrChannelSendRuntimeComponent>[0]['enqueueFromReply'];
19
- listOutboxEntries: Parameters<
20
- typeof createBncrChannelSendRuntimeComponent
21
- >[0]['listOutboxEntries'];
22
- }) {
23
- const channelSendRuntime = createBncrChannelSendRuntimeComponent({
24
- channelId: runtime.channelId,
25
- asString: runtime.asString,
26
- syncDebugFlag: runtime.syncDebugFlag,
27
- logInfo: runtime.logInfo,
28
- resolveVerifiedTarget: runtime.resolveVerifiedTarget,
29
- rememberSessionRoute: runtime.rememberSessionRoute,
30
- enqueueFromReply: runtime.enqueueFromReply,
31
- listOutboxEntries: runtime.listOutboxEntries,
32
- });
33
-
34
- return {
35
- channelSendRuntime,
36
- };
37
- }