@xmoxmo/bncr 0.4.1 → 0.4.3
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 +619 -455
- package/package.json +1 -1
- package/src/bootstrap/runtime-discovery.ts +4 -0
- package/src/bootstrap/runtime-loader.ts +23 -2
- package/src/channel.ts +3 -0
- package/src/core/outbox-entry-builders.ts +2 -0
- package/src/messaging/outbound/build-send-action.ts +21 -5
- package/src/messaging/outbound/media.ts +2 -0
- package/src/messaging/outbound/reply-enqueue-media.ts +3 -0
- package/src/messaging/outbound/reply-enqueue.ts +4 -0
- package/src/messaging/outbound/send-params.ts +27 -5
- package/src/messaging/outbound/send.ts +3 -0
- package/src/plugin/channel-plugin-surface-group.ts +34 -31
- package/src/plugin/channel-runtime-builders-delivery.ts +1 -0
- package/src/plugin/media-orchestrators-runtime-group.ts +2 -0
package/package.json
CHANGED
|
@@ -153,6 +153,10 @@ function ensurePluginNodeModulesLink(pluginDir: string, targetRoot: string) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
export function resolveBncrRuntimeSourceDir(pluginDir: string) {
|
|
156
|
+
const pluginRoot = resolveBncrPluginRoot(pluginDir);
|
|
157
|
+
const rootSource = path.join(pluginRoot, 'src');
|
|
158
|
+
if (fs.existsSync(path.join(rootSource, 'channel.ts'))) return rootSource;
|
|
159
|
+
|
|
156
160
|
const direct = path.join(pluginDir, 'src');
|
|
157
161
|
if (fs.existsSync(path.join(direct, 'channel.ts'))) return direct;
|
|
158
162
|
|
|
@@ -15,9 +15,30 @@ export type LoadedRuntime = {
|
|
|
15
15
|
createBncrChannelPlugin: ChannelModule['createBncrChannelPlugin'];
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export
|
|
18
|
+
export function resolvePluginEntryFileFromModule(moduleUrl: string) {
|
|
19
|
+
const currentFile = fileURLToPath(moduleUrl);
|
|
20
|
+
const pluginRoot = resolveBncrPluginRoot(currentFile);
|
|
21
|
+
const currentDir = path.dirname(currentFile);
|
|
22
|
+
const distEntry = path.join(pluginRoot, 'dist', 'index.js');
|
|
23
|
+
if (currentFile === distEntry && fs.existsSync(distEntry)) return distEntry;
|
|
24
|
+
|
|
25
|
+
const sourceEntry = path.join(pluginRoot, 'index.ts');
|
|
26
|
+
if (fs.existsSync(sourceEntry)) return sourceEntry;
|
|
27
|
+
|
|
28
|
+
if (fs.existsSync(distEntry)) return distEntry;
|
|
29
|
+
|
|
30
|
+
if (path.basename(currentDir) === 'dist') return distEntry;
|
|
31
|
+
|
|
32
|
+
return sourceEntry;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolvePluginEntryFile() {
|
|
36
|
+
return resolvePluginEntryFileFromModule(import.meta.url);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const pluginFile = resolvePluginEntryFile();
|
|
19
40
|
export const pluginDir = path.dirname(pluginFile);
|
|
20
|
-
export const pluginRequire = createRequire(
|
|
41
|
+
export const pluginRequire = createRequire(pluginFile);
|
|
21
42
|
export const pluginRoot = resolveBncrPluginRoot(pluginFile);
|
|
22
43
|
|
|
23
44
|
const runtimeSourceDir = resolveBncrRuntimeSourceDir(pluginDir);
|
package/src/channel.ts
CHANGED
|
@@ -1164,6 +1164,7 @@ class BncrBridgeRuntime {
|
|
|
1164
1164
|
asVoice?: boolean;
|
|
1165
1165
|
audioAsVoice?: boolean;
|
|
1166
1166
|
type?: string;
|
|
1167
|
+
extra?: Record<string, unknown>;
|
|
1167
1168
|
kind?: 'tool' | 'block' | 'final';
|
|
1168
1169
|
replyToId?: string;
|
|
1169
1170
|
replyTargetPolicy?: OutboundReplyTargetPolicy;
|
|
@@ -1182,6 +1183,7 @@ class BncrBridgeRuntime {
|
|
|
1182
1183
|
asVoice: params.asVoice,
|
|
1183
1184
|
audioAsVoice: params.audioAsVoice,
|
|
1184
1185
|
type: params.type,
|
|
1186
|
+
extra: params.extra,
|
|
1185
1187
|
kind: params.kind,
|
|
1186
1188
|
replyToId: asString(params.replyToId || '').trim() || undefined,
|
|
1187
1189
|
replyTargetPolicy: params.replyTargetPolicy,
|
|
@@ -1245,6 +1247,7 @@ class BncrBridgeRuntime {
|
|
|
1245
1247
|
mimeType: params.media.mimeType,
|
|
1246
1248
|
}),
|
|
1247
1249
|
hintedType: wantsVoice ? 'voice' : asString(params.meta.type || '') || undefined,
|
|
1250
|
+
extra: params.meta.extra as Record<string, unknown> | undefined,
|
|
1248
1251
|
kind: messageKind,
|
|
1249
1252
|
replyToId: normalizeReplyToId(params.meta.replyToId) || undefined,
|
|
1250
1253
|
now: now(),
|
|
@@ -16,6 +16,7 @@ export function buildFileTransferOutboxEntry(args: {
|
|
|
16
16
|
asVoice?: boolean;
|
|
17
17
|
audioAsVoice?: boolean;
|
|
18
18
|
type?: string;
|
|
19
|
+
extra?: Record<string, unknown>;
|
|
19
20
|
kind?: 'tool' | 'block' | 'final';
|
|
20
21
|
replyToId?: string;
|
|
21
22
|
replyTargetPolicy?: OutboundReplyTargetPolicy;
|
|
@@ -38,6 +39,7 @@ export function buildFileTransferOutboxEntry(args: {
|
|
|
38
39
|
asVoice: args.asVoice === true,
|
|
39
40
|
audioAsVoice: args.audioAsVoice === true,
|
|
40
41
|
type: args.type,
|
|
42
|
+
...(args.extra ? { extra: { ...args.extra } } : {}),
|
|
41
43
|
finalEvent: args.pushEvent,
|
|
42
44
|
replyToId:
|
|
43
45
|
normalizeOutboundReplyToId({
|
|
@@ -12,6 +12,7 @@ type MinimalBncrSendInput = {
|
|
|
12
12
|
media?: string;
|
|
13
13
|
filePath?: string;
|
|
14
14
|
mediaUrl?: string;
|
|
15
|
+
mediaUrls?: string[];
|
|
15
16
|
asVoice?: boolean;
|
|
16
17
|
audioAsVoice?: boolean;
|
|
17
18
|
params?: Record<string, unknown>;
|
|
@@ -73,13 +74,19 @@ export function buildBncrMessageAction(input: MinimalBncrSendInput): BuiltBncrMe
|
|
|
73
74
|
input.filePath,
|
|
74
75
|
input.mediaUrl,
|
|
75
76
|
);
|
|
77
|
+
const rawMediaUrls = Array.isArray(paramsObj.mediaUrls)
|
|
78
|
+
? paramsObj.mediaUrls
|
|
79
|
+
: Array.isArray(input.mediaUrls)
|
|
80
|
+
? input.mediaUrls
|
|
81
|
+
: undefined;
|
|
82
|
+
const mediaUrls = rawMediaUrls?.map((value) => asString(value || '').trim()).filter(Boolean);
|
|
76
83
|
|
|
77
84
|
const message = pickFirstString(paramsObj.message, input.message) ?? '';
|
|
78
85
|
const explicitCaption = pickFirstString(paramsObj.caption, input.caption) ?? '';
|
|
79
86
|
const asVoice = pickFirstBoolean(paramsObj.asVoice, input.asVoice);
|
|
80
87
|
const audioAsVoice = pickFirstBoolean(paramsObj.audioAsVoice, input.audioAsVoice);
|
|
81
88
|
|
|
82
|
-
if ((asVoice === true || audioAsVoice === true) && !mediaPath) {
|
|
89
|
+
if ((asVoice === true || audioAsVoice === true) && !mediaPath && !mediaUrls?.length) {
|
|
83
90
|
throw new Error('bncr voice send requires media path');
|
|
84
91
|
}
|
|
85
92
|
|
|
@@ -93,11 +100,20 @@ export function buildBncrMessageAction(input: MinimalBncrSendInput): BuiltBncrMe
|
|
|
93
100
|
const finalCaption = explicitCaption || message;
|
|
94
101
|
if (finalCaption) normalizedParams.caption = finalCaption;
|
|
95
102
|
delete normalizedParams.message;
|
|
103
|
+
if (mediaUrls?.length) normalizedParams.mediaUrls = mediaUrls;
|
|
96
104
|
} else {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
if (mediaUrls?.length) {
|
|
106
|
+
normalizedParams.mediaUrls = mediaUrls;
|
|
107
|
+
const finalCaption = explicitCaption || message;
|
|
108
|
+
if (finalCaption) normalizedParams.caption = finalCaption;
|
|
109
|
+
delete normalizedParams.message;
|
|
110
|
+
delete normalizedParams.path;
|
|
111
|
+
} else {
|
|
112
|
+
const finalMessage = message || explicitCaption;
|
|
113
|
+
if (!finalMessage.trim()) throw new Error('bncr send requires message or media');
|
|
114
|
+
normalizedParams.message = finalMessage;
|
|
115
|
+
delete normalizedParams.caption;
|
|
116
|
+
}
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
if (asVoice === true) normalizedParams.asVoice = true;
|
|
@@ -68,6 +68,7 @@ export function buildBncrMediaOutboundFrame(params: {
|
|
|
68
68
|
mediaMsg: string;
|
|
69
69
|
fileName: string;
|
|
70
70
|
hintedType?: string;
|
|
71
|
+
extra?: Record<string, unknown>;
|
|
71
72
|
kind?: 'tool' | 'block' | 'final';
|
|
72
73
|
replyToId?: string;
|
|
73
74
|
now: number;
|
|
@@ -96,6 +97,7 @@ export function buildBncrMediaOutboundFrame(params: {
|
|
|
96
97
|
base64: params.media.mediaBase64 || '',
|
|
97
98
|
fileName: params.fileName,
|
|
98
99
|
transferMode: params.media.mode,
|
|
100
|
+
...(params.extra ? { extra: { ...params.extra } } : {}),
|
|
99
101
|
},
|
|
100
102
|
ts: params.now,
|
|
101
103
|
};
|
|
@@ -62,6 +62,7 @@ export function enqueueReplyMediaFileTransferEntry(
|
|
|
62
62
|
asVoice: boolean;
|
|
63
63
|
audioAsVoice: boolean;
|
|
64
64
|
type?: string;
|
|
65
|
+
extra?: Record<string, unknown>;
|
|
65
66
|
kind?: 'tool' | 'block' | 'final';
|
|
66
67
|
replyToId?: string;
|
|
67
68
|
replyTargetPolicy?: OutboundReplyTargetPolicy;
|
|
@@ -86,6 +87,7 @@ export function enqueueReplyMediaFileTransferEntry(
|
|
|
86
87
|
asVoice: params.asVoice,
|
|
87
88
|
audioAsVoice: params.audioAsVoice,
|
|
88
89
|
type: params.type,
|
|
90
|
+
extra: params.extra,
|
|
89
91
|
kind: params.kind,
|
|
90
92
|
replyToId: params.replyToId || undefined,
|
|
91
93
|
replyTargetPolicy: params.replyTargetPolicy,
|
|
@@ -132,6 +134,7 @@ export function enqueueSingleReplyMediaEntry(
|
|
|
132
134
|
asVoice: params.params.payload.asVoice,
|
|
133
135
|
audioAsVoice: params.params.payload.audioAsVoice,
|
|
134
136
|
type: params.params.payload.type,
|
|
137
|
+
extra: params.params.payload.extra,
|
|
135
138
|
kind: params.params.payload.kind,
|
|
136
139
|
replyToId: params.params.payload.replyToId,
|
|
137
140
|
replyTargetPolicy: params.params.payload.replyTargetPolicy,
|
|
@@ -19,6 +19,7 @@ export type ReplyPayloadInput = {
|
|
|
19
19
|
asVoice?: boolean;
|
|
20
20
|
audioAsVoice?: boolean;
|
|
21
21
|
type?: string;
|
|
22
|
+
extra?: Record<string, unknown>;
|
|
22
23
|
kind?: 'tool' | 'block' | 'final';
|
|
23
24
|
replyToId?: string;
|
|
24
25
|
};
|
|
@@ -31,6 +32,7 @@ export type NormalizedReplyPayload = {
|
|
|
31
32
|
asVoice: boolean;
|
|
32
33
|
audioAsVoice: boolean;
|
|
33
34
|
type?: string;
|
|
35
|
+
extra?: Record<string, unknown>;
|
|
34
36
|
kind?: 'tool' | 'block' | 'final';
|
|
35
37
|
replyToId: string;
|
|
36
38
|
replyTargetPolicy: OutboundReplyTargetPolicy;
|
|
@@ -64,6 +66,7 @@ export type ReplyMediaFileTransferParams = {
|
|
|
64
66
|
asVoice: boolean;
|
|
65
67
|
audioAsVoice: boolean;
|
|
66
68
|
type?: string;
|
|
69
|
+
extra?: Record<string, unknown>;
|
|
67
70
|
kind?: 'tool' | 'block' | 'final';
|
|
68
71
|
replyToId: string;
|
|
69
72
|
replyTargetPolicy: OutboundReplyTargetPolicy;
|
|
@@ -268,6 +271,7 @@ export function normalizeReplyPayload(
|
|
|
268
271
|
asVoice: payload?.asVoice === true,
|
|
269
272
|
audioAsVoice: payload?.audioAsVoice === true,
|
|
270
273
|
...(type ? { type } : {}),
|
|
274
|
+
...(payload?.extra ? { extra: { ...payload.extra } } : {}),
|
|
271
275
|
kind: payload?.kind,
|
|
272
276
|
replyTargetPolicy: options?.replyTargetPolicy ?? 'agent-default',
|
|
273
277
|
replyToId: normalizeOutboundReplyToId({
|
|
@@ -7,9 +7,11 @@ export type NormalizedBncrSendParams = {
|
|
|
7
7
|
message: string;
|
|
8
8
|
caption: string;
|
|
9
9
|
mediaUrl?: string;
|
|
10
|
+
mediaUrls?: string[];
|
|
10
11
|
asVoice: boolean;
|
|
11
12
|
audioAsVoice: boolean;
|
|
12
13
|
type?: string;
|
|
14
|
+
extra?: Record<string, unknown>;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
@@ -33,16 +35,34 @@ export function normalizeBncrSendParams(input: {
|
|
|
33
35
|
readOpenClawStringParam(paramsObj, 'path', { trim: false }) ??
|
|
34
36
|
readOpenClawStringParam(paramsObj, 'filePath', { trim: false }) ??
|
|
35
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;
|
|
36
52
|
const asVoice = readOpenClawBooleanParam(paramsObj, 'asVoice') ?? false;
|
|
37
53
|
const audioAsVoice = readOpenClawBooleanParam(paramsObj, 'audioAsVoice') ?? false;
|
|
38
54
|
const type = readOpenClawStringParam(paramsObj, 'type') || undefined;
|
|
55
|
+
const rawExtra = paramsObj.extra;
|
|
56
|
+
const extra = isPlainObject(rawExtra) ? { ...rawExtra } : undefined;
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
const hasMedia = Boolean(mediaUrl || dedupedMediaUrls?.length);
|
|
41
59
|
|
|
42
|
-
|
|
43
|
-
const normalizedCaption = mediaUrl ? caption || message || '' : '';
|
|
60
|
+
if (asVoice && !hasMedia) throw new Error('send voice requires media path');
|
|
44
61
|
|
|
45
|
-
|
|
62
|
+
const normalizedMessage = hasMedia ? '' : message || caption || '';
|
|
63
|
+
const normalizedCaption = hasMedia ? caption || message || '' : '';
|
|
64
|
+
|
|
65
|
+
if (!normalizedMessage.trim() && !normalizedCaption.trim() && !hasMedia) {
|
|
46
66
|
throw new Error('send requires message or media');
|
|
47
67
|
}
|
|
48
68
|
|
|
@@ -51,9 +71,11 @@ export function normalizeBncrSendParams(input: {
|
|
|
51
71
|
accountId: resolvedAccountId,
|
|
52
72
|
message: normalizedMessage,
|
|
53
73
|
caption: normalizedCaption,
|
|
54
|
-
mediaUrl: mediaUrl || undefined,
|
|
74
|
+
mediaUrl: dedupedMediaUrls?.length ? undefined : mediaUrl || undefined,
|
|
75
|
+
mediaUrls: dedupedMediaUrls,
|
|
55
76
|
asVoice,
|
|
56
77
|
audioAsVoice,
|
|
57
78
|
...(type ? { type } : {}),
|
|
79
|
+
...(extra ? { extra } : {}),
|
|
58
80
|
};
|
|
59
81
|
}
|
|
@@ -64,6 +64,7 @@ export async function sendBncrMedia(params: {
|
|
|
64
64
|
asVoice?: boolean;
|
|
65
65
|
audioAsVoice?: boolean;
|
|
66
66
|
type?: string;
|
|
67
|
+
extra?: Record<string, unknown>;
|
|
67
68
|
kind?: string;
|
|
68
69
|
replyToId?: string;
|
|
69
70
|
mediaLocalRoots?: readonly string[];
|
|
@@ -83,6 +84,7 @@ export async function sendBncrMedia(params: {
|
|
|
83
84
|
asVoice?: boolean;
|
|
84
85
|
audioAsVoice?: boolean;
|
|
85
86
|
type?: string;
|
|
87
|
+
extra?: Record<string, unknown>;
|
|
86
88
|
kind?: 'tool' | 'block' | 'final';
|
|
87
89
|
replyToId?: string;
|
|
88
90
|
};
|
|
@@ -104,6 +106,7 @@ export async function sendBncrMedia(params: {
|
|
|
104
106
|
asVoice: params.asVoice === true ? true : undefined,
|
|
105
107
|
audioAsVoice: params.audioAsVoice === true ? true : undefined,
|
|
106
108
|
type: params.type,
|
|
109
|
+
extra: params.extra,
|
|
107
110
|
kind: normalizeReplyKind(params.kind),
|
|
108
111
|
replyToId: params.replyToId,
|
|
109
112
|
},
|
|
@@ -140,37 +140,40 @@ export function createBncrChannelPluginSurfaceGroup(runtime: {
|
|
|
140
140
|
const normalized = normalizeBncrSendParams({ params, accountId: accountId || '' });
|
|
141
141
|
|
|
142
142
|
const toolActionBridge = runtime.getToolActionBridge();
|
|
143
|
-
const result =
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
143
|
+
const result =
|
|
144
|
+
normalized.mediaUrl || normalized.mediaUrls?.length
|
|
145
|
+
? await sendBncrMedia({
|
|
146
|
+
channelId: runtime.channelId,
|
|
147
|
+
accountId: normalized.accountId,
|
|
148
|
+
to: normalized.to,
|
|
149
|
+
text: normalized.caption,
|
|
150
|
+
mediaUrl: normalized.mediaUrl,
|
|
151
|
+
mediaUrls: normalized.mediaUrls,
|
|
152
|
+
asVoice: normalized.asVoice,
|
|
153
|
+
audioAsVoice: normalized.audioAsVoice,
|
|
154
|
+
type: normalized.type,
|
|
155
|
+
extra: normalized.extra,
|
|
156
|
+
mediaLocalRoots,
|
|
157
|
+
resolveVerifiedTarget: (to, accountId) =>
|
|
158
|
+
toolActionBridge.resolveVerifiedTarget(to, accountId),
|
|
159
|
+
rememberSessionRoute: (sessionKey, accountId, route) =>
|
|
160
|
+
toolActionBridge.rememberSessionRoute(sessionKey, accountId, route),
|
|
161
|
+
enqueueFromReply: (args) => toolActionBridge.enqueueFromReply(args),
|
|
162
|
+
createMessageId: () => randomUUID(),
|
|
163
|
+
})
|
|
164
|
+
: await sendBncrText({
|
|
165
|
+
channelId: runtime.channelId,
|
|
166
|
+
accountId: normalized.accountId,
|
|
167
|
+
to: normalized.to,
|
|
168
|
+
text: normalized.message,
|
|
169
|
+
mediaLocalRoots,
|
|
170
|
+
resolveVerifiedTarget: (to, accountId) =>
|
|
171
|
+
toolActionBridge.resolveVerifiedTarget(to, accountId),
|
|
172
|
+
rememberSessionRoute: (sessionKey, accountId, route) =>
|
|
173
|
+
toolActionBridge.rememberSessionRoute(sessionKey, accountId, route),
|
|
174
|
+
enqueueFromReply: (args) => toolActionBridge.enqueueFromReply(args),
|
|
175
|
+
createMessageId: () => randomUUID(),
|
|
176
|
+
});
|
|
174
177
|
|
|
175
178
|
return runtime.openClawJsonResult({ ok: true, ...result });
|
|
176
179
|
},
|
|
@@ -337,6 +337,7 @@ export function buildBncrMediaOrchestratorsRuntime(deps: {
|
|
|
337
337
|
asVoice: boolean;
|
|
338
338
|
audioAsVoice: boolean;
|
|
339
339
|
type?: string;
|
|
340
|
+
extra?: Record<string, unknown>;
|
|
340
341
|
kind?: 'tool' | 'block' | 'final';
|
|
341
342
|
replyToId?: string;
|
|
342
343
|
replyTargetPolicy?: OutboundReplyTargetPolicy;
|
|
@@ -49,6 +49,7 @@ function buildReplyMediaEntryHelpers(runtime: {
|
|
|
49
49
|
asVoice: boolean;
|
|
50
50
|
audioAsVoice: boolean;
|
|
51
51
|
type?: string;
|
|
52
|
+
extra?: Record<string, unknown>;
|
|
52
53
|
kind?: 'tool' | 'block' | 'final';
|
|
53
54
|
replyToId?: string;
|
|
54
55
|
replyTargetPolicy?: OutboundReplyTargetPolicy;
|
|
@@ -203,6 +204,7 @@ export function createBncrMediaOrchestratorsRuntimeGroup(runtime: {
|
|
|
203
204
|
asVoice: boolean;
|
|
204
205
|
audioAsVoice: boolean;
|
|
205
206
|
type?: string;
|
|
207
|
+
extra?: Record<string, unknown>;
|
|
206
208
|
kind?: 'tool' | 'block' | 'final';
|
|
207
209
|
replyToId?: string;
|
|
208
210
|
replyTargetPolicy?: OutboundReplyTargetPolicy;
|