@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.
- package/dist/index.js +1 -9
- package/index.ts +1 -9
- package/package.json +1 -1
- package/scripts/check-pack.mjs +0 -8
- package/scripts/selfcheck.mjs +0 -10
- package/src/bootstrap/runtime-loader.ts +1 -1
- package/src/channel.ts +118 -171
- package/src/core/accounts.ts +1 -6
- package/src/core/connection-reachability.ts +2 -6
- package/src/core/dead-letter-diagnostics.ts +4 -10
- package/src/core/logging.ts +1 -1
- package/src/core/outbox-entry-builders.ts +78 -84
- package/src/core/outbox-file-transfer-failure.ts +5 -0
- package/src/core/outbox-file-transfer-guards.ts +6 -0
- package/src/core/outbox-file-transfer-prep.ts +8 -5
- package/src/core/outbox-file-transfer-success.ts +5 -0
- package/src/core/outbox-queue.ts +1 -7
- package/src/core/outbox-summary.ts +10 -56
- package/src/core/permissions.ts +2 -10
- package/src/core/persisted-outbox-entry.ts +54 -11
- package/src/core/policy.ts +1 -11
- package/src/core/register-trace.ts +4 -7
- package/src/core/status-meta.ts +1 -9
- package/src/core/status.ts +1 -21
- package/src/core/targets.ts +52 -70
- package/src/core/types.ts +1 -17
- package/src/core/value-sanitize.ts +38 -1
- package/src/messaging/inbound/commands.ts +2 -24
- package/src/messaging/inbound/dispatch-prep.ts +1 -1
- package/src/messaging/inbound/gate.ts +2 -31
- package/src/messaging/inbound/group-history.ts +0 -75
- package/src/messaging/inbound/parse.ts +2 -32
- package/src/messaging/inbound/scene-admin.ts +8 -8
- package/src/messaging/outbound/diagnostics-debug-builders.ts +0 -15
- package/src/messaging/outbound/diagnostics.ts +1 -7
- package/src/messaging/outbound/durable-queue-adapter.ts +17 -11
- package/src/messaging/outbound/marker-parser.ts +277 -28
- package/src/messaging/outbound/media-dedupe.ts +3 -10
- package/src/messaging/outbound/media.ts +1 -6
- package/src/messaging/outbound/normalize-outbound-send.ts +329 -0
- package/src/messaging/outbound/queue-selectors.ts +1 -5
- package/src/messaging/outbound/reply-enqueue-media.ts +1 -184
- package/src/messaging/outbound/reply-enqueue.ts +313 -175
- package/src/messaging/outbound/session-route.ts +6 -11
- package/src/messaging/outbound/target-resolver.ts +1 -6
- package/src/openclaw/media-runtime.ts +1 -1
- package/src/openclaw/routing-runtime.ts +0 -11
- package/src/openclaw/sdk-helpers.ts +0 -2
- package/src/plugin/bridge-media-facade.ts +0 -4
- package/src/plugin/bridge-outbox-facade.ts +0 -1
- package/src/plugin/channel-components.ts +0 -13
- package/src/plugin/channel-plugin-surface-group.ts +52 -65
- package/src/plugin/channel-runtime-builders-delivery.ts +3 -362
- package/src/plugin/channel-runtime-builders-status.ts +0 -160
- package/src/plugin/channel-runtime-builders.ts +3 -17
- package/src/plugin/channel-runtime-constants.ts +1 -1
- package/src/plugin/channel-runtime-types.ts +2 -24
- package/src/plugin/channel-send.ts +206 -221
- package/src/plugin/channel-utils.ts +8 -29
- package/src/plugin/connection-handlers-helpers.ts +0 -15
- package/src/plugin/connection-handlers.ts +6 -5
- package/src/plugin/file-inbound-abort.ts +5 -0
- package/src/plugin/file-inbound-chunk.ts +5 -0
- package/src/plugin/file-inbound-complete.ts +5 -0
- package/src/plugin/file-transfer-orchestrator.ts +39 -2
- package/src/plugin/media-dedupe-runtime.ts +1 -6
- package/src/plugin/media-orchestrators-runtime-group.ts +20 -149
- package/src/plugin/message-send.ts +7 -8
- package/src/plugin/messaging.ts +3 -3
- package/src/plugin/outbound.ts +6 -26
- package/src/plugin/outbox-drain-loop.ts +2 -2
- package/src/plugin/outbox-push.ts +1 -52
- package/src/plugin/outbox-unified-push-flow.ts +307 -0
- package/src/plugin/scene-registry.ts +1 -1
- package/src/plugin/target-runtime.ts +1 -6
- package/src/runtime/log-dedupe.ts +3 -3
- package/src/runtime/outbound-ack-timeout.ts +3 -12
- package/src/runtime/outbox-transitions.ts +1 -4
- package/src/runtime/status-snapshots.ts +2 -2
- package/src/runtime/status-worker.ts +3 -15
- package/src/core/outbox-text-push-prep.ts +0 -36
- package/src/messaging/outbound/build-send-action.ts +0 -130
- package/src/messaging/outbound/durable-message-adapter.ts +0 -121
- package/src/messaging/outbound/send-params.ts +0 -114
- package/src/messaging/outbound/send.ts +0 -127
- package/src/openclaw/ingress-runtime.ts +0 -25
- package/src/plugin/bridge-file-transfer-push-facade.ts +0 -142
- package/src/plugin/channel-send-runtime-group.ts +0 -37
- package/src/plugin/outbox-file-push-flow.ts +0 -69
- 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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
if (
|
|
154
|
-
|
|
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
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
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 {
|
|
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
|
}
|