@xmoxmo/bncr 0.4.5 → 0.4.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 (36) hide show
  1. package/dist/index.js +6 -0
  2. package/index.ts +6 -0
  3. package/package.json +1 -1
  4. package/src/channel.ts +41 -2
  5. package/src/core/targets.ts +106 -17
  6. package/src/messaging/inbound/commands.ts +263 -51
  7. package/src/messaging/inbound/context-facts.ts +126 -14
  8. package/src/messaging/inbound/contracts.ts +24 -0
  9. package/src/messaging/inbound/dispatch-prep.ts +214 -39
  10. package/src/messaging/inbound/dispatch.ts +71 -5
  11. package/src/messaging/inbound/gate.ts +56 -86
  12. package/src/messaging/inbound/group-history.ts +189 -0
  13. package/src/messaging/inbound/native-command-runtime.ts +77 -61
  14. package/src/messaging/inbound/native-command.ts +92 -8
  15. package/src/messaging/inbound/parse.ts +113 -8
  16. package/src/messaging/inbound/reply-dispatch-serial.ts +62 -0
  17. package/src/messaging/inbound/reply-dispatch.ts +252 -77
  18. package/src/messaging/inbound/scene-admin.ts +269 -0
  19. package/src/messaging/inbound/session-label.ts +122 -13
  20. package/src/messaging/inbound/session-meta-task.ts +17 -0
  21. package/src/messaging/inbound/turn-context.ts +184 -71
  22. package/src/openclaw/channel-runtime-contracts.ts +1 -0
  23. package/src/plugin/channel-components.ts +34 -1
  24. package/src/plugin/channel-inbound-helpers.ts +9 -2
  25. package/src/plugin/channel-runtime-builders-delivery.ts +24 -1
  26. package/src/plugin/channel-runtime-types.ts +42 -0
  27. package/src/plugin/file-inbound-init.ts +27 -12
  28. package/src/plugin/file-inbound-runtime.ts +2 -0
  29. package/src/plugin/inbound-acceptance.ts +82 -1
  30. package/src/plugin/inbound-handlers.ts +55 -2
  31. package/src/plugin/inbound-surface-handlers-group.ts +16 -0
  32. package/src/plugin/messaging.ts +22 -5
  33. package/src/plugin/scene-registry.ts +155 -0
  34. package/src/plugin/state-store.ts +133 -0
  35. package/src/plugin/state-transient-runtime-group.ts +5 -0
  36. package/src/plugin/target-runtime.ts +2 -2
@@ -20,7 +20,12 @@ import type {
20
20
  } from './contracts.ts';
21
21
  import type { ParsedInbound } from './dispatch-prep.ts';
22
22
  import { buildBncrInboundRecordUpdateLastRoute } from './last-route.ts';
23
- import { parseBncrNativeCommand, resolveBncrNativeVerboseCommand } from './native-command.ts';
23
+ import {
24
+ parseBncrNativeCommand,
25
+ resolveBncrNativeHelpCommand,
26
+ resolveBncrNativeVerboseCommand,
27
+ resolveBncrNativeWhoamiCommand,
28
+ } from './native-command.ts';
24
29
  import {
25
30
  buildBncrNativeCommandSessionState,
26
31
  buildBncrNativeCommandSummary,
@@ -34,11 +39,13 @@ import {
34
39
  } from './native-command-runtime.ts';
35
40
  import { buildBncrReplyConfig } from './reply-config.ts';
36
41
  import { resolveBncrChannelInboundRuntime } from './runtime-compat.ts';
42
+ import { executeSceneAdminCommand, parseSceneAdminCommand } from './scene-admin.ts';
37
43
  import {
38
44
  buildBncrInboundSessionIdentityPatch,
39
45
  recordAndPatchBncrInboundSessionEntry,
40
46
  wrapBncrInboundRecordSessionLabelCorrection,
41
47
  } from './session-label.ts';
48
+ import { createBncrSessionMetaTaskBarrier } from './session-meta-task.ts';
42
49
 
43
50
  function assertResolvedAgentRoute(resolvedRoute: OpenClawResolvedAgentRoute): {
44
51
  sessionKey: string;
@@ -65,8 +72,9 @@ function buildBncrNativeCommandResolvedRoute(args: {
65
72
  channelId: string;
66
73
  accountId: string;
67
74
  peer: ParsedInbound['peer'];
75
+ resolvedAgentId?: string;
68
76
  }) {
69
- return assertResolvedAgentRoute(
77
+ const resolvedRoute = assertResolvedAgentRoute(
70
78
  resolveOpenClawAgentRoute(args.api, {
71
79
  cfg: args.cfg,
72
80
  channel: args.channelId,
@@ -74,6 +82,14 @@ function buildBncrNativeCommandResolvedRoute(args: {
74
82
  peer: args.peer,
75
83
  }),
76
84
  );
85
+
86
+ const agentId = (args.resolvedAgentId || '').trim() || resolvedRoute.agentId;
87
+ if (!agentId || agentId === resolvedRoute.agentId) return resolvedRoute;
88
+
89
+ return {
90
+ ...resolvedRoute,
91
+ agentId,
92
+ };
77
93
  }
78
94
 
79
95
  export { parseBncrNativeCommand } from './native-command.ts';
@@ -84,6 +100,11 @@ export async function handleBncrNativeCommand(params: {
84
100
  cfg: BncrInboundConfig;
85
101
  parsed: ParsedInbound;
86
102
  canonicalAgentId: string;
103
+ resolvedAgentId?: string;
104
+ sceneRegistry: Map<string, import('../../plugin/channel-runtime-types.ts').BncrSceneRecord>;
105
+ defaultAdminAgentId: string;
106
+ defaultPublicAgentId: string;
107
+ now: () => number;
87
108
  rememberSessionRoute: BncrRememberSessionRoute;
88
109
  enqueueFromReply: BncrEnqueueFromReply;
89
110
  logger?: BncrInboundLogger;
@@ -91,10 +112,24 @@ export async function handleBncrNativeCommand(params: {
91
112
  | { handled: false }
92
113
  | { handled: true; command: string; sessionKey: string; fallbackToAgent?: boolean }
93
114
  > {
94
- const { api, channelId, cfg, parsed, canonicalAgentId, rememberSessionRoute, enqueueFromReply } =
95
- params;
115
+ const {
116
+ api,
117
+ channelId,
118
+ cfg,
119
+ parsed,
120
+ canonicalAgentId,
121
+ resolvedAgentId,
122
+ sceneRegistry,
123
+ defaultAdminAgentId,
124
+ defaultPublicAgentId,
125
+ now,
126
+ rememberSessionRoute,
127
+ enqueueFromReply,
128
+ } = params;
96
129
  const { accountId, route, peer, clientId, extracted, msgId } = parsed;
97
- const command = parseBncrNativeCommand(extracted.text);
130
+ const command = parseBncrNativeCommand(extracted.text, {
131
+ allowBareWhoami: parsed.isAdmin !== true,
132
+ });
98
133
  if (!command) return { handled: false };
99
134
  const nativeCommandDebugEnabled = resolveNativeCommandDebugEnabled({ cfg, channelId });
100
135
 
@@ -115,12 +150,13 @@ export async function handleBncrNativeCommand(params: {
115
150
  channelId,
116
151
  accountId,
117
152
  peer,
153
+ resolvedAgentId,
118
154
  });
119
155
 
120
156
  const { baseSessionKey, taskSessionKey, sessionKey, displayTo, originatingTo } =
121
157
  buildBncrNativeCommandSessionState({
122
158
  parsed,
123
- canonicalAgentId,
159
+ sessionAgentId: resolvedRoute.agentId || canonicalAgentId,
124
160
  resolvedRoute,
125
161
  });
126
162
  rememberSessionRoute(baseSessionKey, accountId, route);
@@ -133,8 +169,8 @@ export async function handleBncrNativeCommand(params: {
133
169
  '[bncr] inbound missing clientId for native command identity; using route identity fallback',
134
170
  );
135
171
  }
136
- const senderIdForContext = clientId || displayTo;
137
- const senderDisplayName = clientId ? 'bncr-client' : displayTo;
172
+ const senderIdForContext = parsed.userId || clientId || displayTo;
173
+ const senderDisplayName = parsed.userName || displayTo;
138
174
  const storePath = resolveBncrInboundSessionStorePath({
139
175
  storeConfig: cfg?.session?.store,
140
176
  agentId: resolvedRoute.agentId,
@@ -166,7 +202,108 @@ export async function handleBncrNativeCommand(params: {
166
202
  });
167
203
 
168
204
  const nativeVerbose = resolveBncrNativeVerboseCommand(command);
205
+ const nativeHelp = resolveBncrNativeHelpCommand(command);
206
+ const nativeWhoami = resolveBncrNativeWhoamiCommand({
207
+ command,
208
+ platform: parsed.platform,
209
+ groupId: parsed.groupId,
210
+ groupName: parsed.groupName,
211
+ userId: parsed.userId,
212
+ userName: parsed.userName,
213
+ isGroup: parsed.isGroup,
214
+ isAdmin: parsed.isAdmin,
215
+ });
216
+ if (nativeHelp) {
217
+ logBncrNativeCommandSummary(
218
+ buildBncrNativeCommandSummary({
219
+ kind: 'help',
220
+ command: command.command,
221
+ accountId,
222
+ to: displayTo,
223
+ msgId: msgId || null,
224
+ result: 'handled',
225
+ }),
226
+ );
227
+ await recordAndPatchBncrInboundSessionEntry({
228
+ storePath,
229
+ sessionKey,
230
+ ctx: ctxPayload,
231
+ patch: sessionIdentityPatch,
232
+ });
233
+ rememberSessionRoute(baseSessionKey, accountId, route);
234
+ await enqueueFromReply({
235
+ accountId,
236
+ sessionKey,
237
+ route,
238
+ payload: {
239
+ text: nativeHelp.text,
240
+ replyToId: msgId || undefined,
241
+ },
242
+ });
243
+ return { handled: true, command: command.command, sessionKey };
244
+ }
245
+
246
+ if (nativeWhoami) {
247
+ logBncrNativeCommandSummary(
248
+ buildBncrNativeCommandSummary({
249
+ kind: 'whoami',
250
+ command: command.command,
251
+ accountId,
252
+ to: displayTo,
253
+ msgId: msgId || null,
254
+ result: 'handled',
255
+ }),
256
+ );
257
+ await recordAndPatchBncrInboundSessionEntry({
258
+ storePath,
259
+ sessionKey,
260
+ ctx: ctxPayload,
261
+ patch: sessionIdentityPatch,
262
+ });
263
+ rememberSessionRoute(baseSessionKey, accountId, route);
264
+ await enqueueFromReply({
265
+ accountId,
266
+ sessionKey,
267
+ route,
268
+ payload: {
269
+ text: nativeWhoami.text,
270
+ replyToId: msgId || undefined,
271
+ },
272
+ });
273
+ return { handled: true, command: command.command, sessionKey };
274
+ }
275
+
169
276
  if (nativeVerbose) {
277
+ if (!parsed.isAdmin) {
278
+ logBncrNativeCommandSummary(
279
+ buildBncrNativeCommandSummary({
280
+ kind: 'verbose',
281
+ command: command.command,
282
+ accountId,
283
+ to: displayTo,
284
+ msgId: msgId || null,
285
+ result: 'rejected',
286
+ }),
287
+ );
288
+ await recordAndPatchBncrInboundSessionEntry({
289
+ storePath,
290
+ sessionKey,
291
+ ctx: ctxPayload,
292
+ patch: sessionIdentityPatch,
293
+ });
294
+ rememberSessionRoute(baseSessionKey, accountId, route);
295
+ await enqueueFromReply({
296
+ accountId,
297
+ sessionKey,
298
+ route,
299
+ payload: {
300
+ text: 'Admin permission required.',
301
+ replyToId: msgId || undefined,
302
+ },
303
+ });
304
+ return { handled: true, command: command.command, sessionKey };
305
+ }
306
+
170
307
  logBncrNativeCommandSummary(
171
308
  buildBncrNativeCommandSummary({
172
309
  kind: 'verbose',
@@ -211,6 +348,73 @@ export async function handleBncrNativeCommand(params: {
211
348
  return { handled: true, command: command.command, sessionKey };
212
349
  }
213
350
 
351
+ const sceneAdmin = parseSceneAdminCommand(command);
352
+ if (sceneAdmin.matched) {
353
+ if (!sceneAdmin.valid) {
354
+ logBncrNativeCommandSummary(
355
+ buildBncrNativeCommandSummary({
356
+ kind: 'scene-admin',
357
+ command: command.command,
358
+ accountId,
359
+ to: displayTo,
360
+ msgId: msgId ?? null,
361
+ result: 'rejected',
362
+ }),
363
+ );
364
+ await recordAndPatchBncrInboundSessionEntry({
365
+ storePath,
366
+ sessionKey,
367
+ ctx: ctxPayload,
368
+ patch: sessionIdentityPatch,
369
+ });
370
+ await enqueueFromReply({
371
+ accountId,
372
+ sessionKey,
373
+ route,
374
+ payload: {
375
+ text: sceneAdmin.text,
376
+ replyToId: msgId || undefined,
377
+ },
378
+ });
379
+ return { handled: true, command: command.command, sessionKey };
380
+ }
381
+
382
+ const outcome = executeSceneAdminCommand({
383
+ parsed,
384
+ command: sceneAdmin.command,
385
+ sceneRegistry,
386
+ defaultAdminAgentId,
387
+ defaultPublicAgentId,
388
+ now,
389
+ });
390
+ logBncrNativeCommandSummary(
391
+ buildBncrNativeCommandSummary({
392
+ kind: 'scene-admin',
393
+ command: command.command,
394
+ accountId,
395
+ to: displayTo,
396
+ msgId: msgId ?? null,
397
+ result: outcome.ok ? 'handled' : 'rejected',
398
+ }),
399
+ );
400
+ await recordAndPatchBncrInboundSessionEntry({
401
+ storePath,
402
+ sessionKey,
403
+ ctx: ctxPayload,
404
+ patch: sessionIdentityPatch,
405
+ });
406
+ await enqueueFromReply({
407
+ accountId,
408
+ sessionKey,
409
+ route,
410
+ payload: {
411
+ text: outcome.text,
412
+ replyToId: msgId || undefined,
413
+ },
414
+ });
415
+ return { handled: true, command: command.command, sessionKey };
416
+ }
417
+
214
418
  await recordAndPatchBncrInboundSessionEntry({
215
419
  storePath,
216
420
  sessionKey,
@@ -264,51 +468,59 @@ export async function handleBncrNativeCommand(params: {
264
468
  textForCommands: ctxPayload.CommandBody,
265
469
  raw: parsed,
266
470
  }),
267
- resolveTurn: () => ({
268
- channel: channelId,
269
- accountId,
270
- routeSessionKey: resolvedRoute.sessionKey,
271
- storePath,
272
- ctxPayload,
273
- recordInboundSession: wrapBncrInboundRecordSessionLabelCorrection({
274
- recordInboundSession: recordBncrInboundSession as (
275
- ...args: unknown[]
276
- ) => Promise<unknown> | unknown,
277
- expectedLabel: displayTo,
278
- }),
279
- record: {
280
- updateLastRoute,
281
- onRecordError: (err: unknown) => {
282
- buildNativeCommandRecordErrorLogger(err);
283
- },
284
- },
285
- runDispatch: () =>
286
- dispatchOpenClawReplyWithBufferedBlockDispatcher(api, {
287
- ctx: ctxPayload,
288
- cfg: effectiveReply.replyCfg,
289
- dispatcherOptions: {
290
- deliver: createNativeCommandReplyDeliverer({
291
- command: command.command,
292
- accountId,
293
- sessionKey,
294
- to: displayTo,
295
- msgId: msgId || undefined,
296
- effectiveReply,
297
- route,
298
- enqueueFromReply,
299
- nativeCommandDebugEnabled,
300
- onResponded: () => responded,
301
- markResponded: () => {
302
- responded = true;
303
- },
304
- }),
471
+ resolveTurn: () => {
472
+ const sessionMetaBarrier = createBncrSessionMetaTaskBarrier();
473
+ return {
474
+ channel: channelId,
475
+ accountId,
476
+ routeSessionKey: resolvedRoute.sessionKey,
477
+ storePath,
478
+ ctxPayload,
479
+ recordInboundSession: wrapBncrInboundRecordSessionLabelCorrection({
480
+ recordInboundSession: recordBncrInboundSession as (
481
+ ...args: unknown[]
482
+ ) => Promise<unknown> | unknown,
483
+ expectedPatch: sessionIdentityPatch,
484
+ }),
485
+ record: {
486
+ updateLastRoute,
487
+ onRecordError: (err: unknown) => {
488
+ buildNativeCommandRecordErrorLogger(err);
305
489
  },
306
- replyOptions: {
307
- disableBlockStreaming: !effectiveReply.blockStreaming,
308
- shouldEmitToolResult: effectiveReply.allowTool ? () => true : undefined,
490
+ trackSessionMetaTask: (task: Promise<unknown>) => {
491
+ sessionMetaBarrier.track(task);
309
492
  },
310
- }),
311
- }),
493
+ },
494
+ runDispatch: async () => {
495
+ await sessionMetaBarrier.wait();
496
+ return dispatchOpenClawReplyWithBufferedBlockDispatcher(api, {
497
+ ctx: ctxPayload,
498
+ cfg: effectiveReply.replyCfg,
499
+ dispatcherOptions: {
500
+ deliver: createNativeCommandReplyDeliverer({
501
+ command: command.command,
502
+ accountId,
503
+ sessionKey,
504
+ to: displayTo,
505
+ msgId: msgId || undefined,
506
+ effectiveReply,
507
+ route,
508
+ enqueueFromReply,
509
+ nativeCommandDebugEnabled,
510
+ onResponded: () => responded,
511
+ markResponded: () => {
512
+ responded = true;
513
+ },
514
+ }),
515
+ },
516
+ replyOptions: {
517
+ disableBlockStreaming: !effectiveReply.blockStreaming,
518
+ shouldEmitToolResult: effectiveReply.allowTool ? () => true : undefined,
519
+ },
520
+ });
521
+ },
522
+ };
523
+ },
312
524
  },
313
525
  });
314
526
 
@@ -19,6 +19,27 @@ export type BncrStructuredContextFactsInput = {
19
19
  sender: {
20
20
  id: string;
21
21
  displayName?: string;
22
+ userId?: string;
23
+ userName?: string;
24
+ bridgeId?: string;
25
+ bridgeName?: string;
26
+ isAdmin?: boolean;
27
+ isOwner?: boolean;
28
+ isAuthorizedSender?: boolean;
29
+ role?: 'owner' | 'admin' | 'user';
30
+ };
31
+ platform?: string;
32
+ group?: {
33
+ id?: string;
34
+ name?: string;
35
+ isGroup?: boolean;
36
+ };
37
+ trigger?: {
38
+ shouldRespond?: boolean;
39
+ triggerKind?: string;
40
+ botName?: string;
41
+ isBotMentioned?: boolean;
42
+ isReplyToBot?: boolean;
22
43
  };
23
44
  message: {
24
45
  id?: string | null;
@@ -60,6 +81,27 @@ export function buildBncrStructuredContextFacts(input: BncrStructuredContextFact
60
81
  sender: {
61
82
  id: input.sender.id,
62
83
  displayName: input.sender.displayName || input.sender.id,
84
+ userId: input.sender.userId,
85
+ userName: input.sender.userName,
86
+ bridgeId: input.sender.bridgeId,
87
+ bridgeName: input.sender.bridgeName,
88
+ isAdmin: input.sender.isAdmin,
89
+ ...(input.sender.isOwner === true ? { isOwner: true } : {}),
90
+ ...(input.sender.isAuthorizedSender === true ? { isAuthorizedSender: true } : {}),
91
+ ...(input.sender.role !== undefined ? { role: input.sender.role } : {}),
92
+ },
93
+ platform: input.platform,
94
+ group: {
95
+ id: input.group?.id,
96
+ name: input.group?.name,
97
+ isGroup: input.group?.isGroup,
98
+ },
99
+ trigger: {
100
+ shouldRespond: input.trigger?.shouldRespond,
101
+ triggerKind: input.trigger?.triggerKind,
102
+ botName: input.trigger?.botName,
103
+ isBotMentioned: input.trigger?.isBotMentioned,
104
+ isReplyToBot: input.trigger?.isReplyToBot,
63
105
  },
64
106
  message: {
65
107
  id: input.message.id || undefined,
@@ -85,6 +127,15 @@ export function buildBncrPromptVisibleContextFacts(
85
127
  facts: ReturnType<typeof buildBncrStructuredContextFacts>,
86
128
  ) {
87
129
  const result: {
130
+ trigger?: {
131
+ botName?: string;
132
+ };
133
+ sender?: {
134
+ isAdmin?: boolean;
135
+ isOwner?: boolean;
136
+ isAuthorizedSender?: boolean;
137
+ role?: 'owner' | 'admin' | 'user';
138
+ };
88
139
  reply?: {
89
140
  to: string;
90
141
  originatingTo: string;
@@ -96,6 +147,30 @@ export function buildBncrPromptVisibleContextFacts(
96
147
  }>;
97
148
  } = {};
98
149
 
150
+ if (
151
+ facts.sender.isAdmin === true ||
152
+ facts.sender.isOwner === true ||
153
+ facts.sender.isAuthorizedSender === true ||
154
+ facts.sender.role === 'owner' ||
155
+ facts.sender.role === 'admin'
156
+ ) {
157
+ const sender = {
158
+ ...(facts.sender.isAdmin === true ? { isAdmin: true } : {}),
159
+ ...(facts.sender.isOwner === true ? { isOwner: true } : {}),
160
+ ...(facts.sender.isAuthorizedSender === true ? { isAuthorizedSender: true } : {}),
161
+ ...(facts.sender.role === 'owner' || facts.sender.role === 'admin'
162
+ ? { role: facts.sender.role }
163
+ : {}),
164
+ };
165
+ if (Object.keys(sender).length > 0) result.sender = sender;
166
+ }
167
+
168
+ if (facts.trigger.botName) {
169
+ result.trigger = {
170
+ botName: facts.trigger.botName,
171
+ };
172
+ }
173
+
99
174
  if (facts.reply.originatingTo !== facts.reply.to) {
100
175
  result.reply = {
101
176
  to: facts.reply.to,
@@ -125,11 +200,25 @@ export type BncrStructuredContextFactsFromInboundPartsInput = {
125
200
  channelId: string;
126
201
  parsed: {
127
202
  accountId: string;
203
+ platform?: string;
128
204
  peer: {
129
205
  kind: string;
130
206
  id: string;
131
207
  };
132
208
  clientId?: string;
209
+ bridgeId?: string;
210
+ bridgeName?: string;
211
+ groupId?: string;
212
+ groupName?: string;
213
+ userId?: string;
214
+ userName?: string;
215
+ isGroup?: boolean;
216
+ isAdmin?: boolean;
217
+ shouldRespond?: boolean;
218
+ triggerKind?: string;
219
+ botName?: string;
220
+ isBotMentioned?: boolean;
221
+ isReplyToBot?: boolean;
133
222
  msgId?: string;
134
223
  mimeType?: string;
135
224
  };
@@ -147,18 +236,24 @@ export type BncrStructuredContextFactsFromInboundPartsInput = {
147
236
  prepared: {
148
237
  rawBody: string;
149
238
  body?: string;
150
- mediaPath?: string | null;
151
- mediaContentType?: string;
239
+ mediaItems?: Array<{
240
+ path: string;
241
+ contentType?: string;
242
+ kind?: string;
243
+ }>;
152
244
  };
153
245
  senderIdForContext: string;
154
246
  senderDisplayName?: string;
247
+ bridgeSenderId?: string;
248
+ bridgeSenderName?: string;
249
+ senderIsOwner?: boolean;
250
+ senderIsAuthorized?: boolean;
155
251
  };
156
252
 
157
253
  export function buildBncrStructuredContextFactsFromInboundParts(
158
254
  input: BncrStructuredContextFactsFromInboundPartsInput,
159
255
  ) {
160
- const mediaPath = input.prepared.mediaPath || undefined;
161
- const mediaContentType = input.prepared.mediaContentType || input.parsed.mimeType;
256
+ const mediaItems = Array.isArray(input.prepared.mediaItems) ? input.prepared.mediaItems : [];
162
257
  return buildBncrStructuredContextFacts({
163
258
  channelId: input.channelId,
164
259
  accountId: input.parsed.accountId,
@@ -180,6 +275,27 @@ export function buildBncrStructuredContextFactsFromInboundParts(
180
275
  sender: {
181
276
  id: input.senderIdForContext,
182
277
  displayName: input.senderDisplayName,
278
+ userId: input.parsed.userId,
279
+ userName: input.parsed.userName,
280
+ bridgeId: input.bridgeSenderId || input.parsed.bridgeId || input.parsed.clientId,
281
+ bridgeName: input.bridgeSenderName || input.parsed.bridgeName,
282
+ isAdmin: input.parsed.isAdmin,
283
+ isOwner: input.senderIsOwner === true ? true : undefined,
284
+ isAuthorizedSender: input.senderIsAuthorized === true ? true : undefined,
285
+ role: input.senderIsOwner ? 'owner' : input.parsed.isAdmin ? 'admin' : undefined,
286
+ },
287
+ platform: input.parsed.platform,
288
+ group: {
289
+ id: input.parsed.groupId,
290
+ name: input.parsed.groupName,
291
+ isGroup: input.parsed.isGroup,
292
+ },
293
+ trigger: {
294
+ shouldRespond: input.parsed.shouldRespond,
295
+ triggerKind: input.parsed.triggerKind,
296
+ botName: input.parsed.botName,
297
+ isBotMentioned: input.parsed.isBotMentioned,
298
+ isReplyToBot: input.parsed.isReplyToBot,
183
299
  },
184
300
  message: {
185
301
  id: input.parsed.msgId,
@@ -188,15 +304,11 @@ export function buildBncrStructuredContextFactsFromInboundParts(
188
304
  commandBody: input.prepared.rawBody,
189
305
  envelopeBody: input.prepared.body,
190
306
  },
191
- media: mediaPath
192
- ? [
193
- {
194
- path: mediaPath,
195
- contentType: mediaContentType,
196
- kind: inferBncrStructuredMediaKind(mediaContentType),
197
- messageId: input.parsed.msgId,
198
- },
199
- ]
200
- : [],
307
+ media: mediaItems.map((item) => ({
308
+ path: item.path,
309
+ contentType: item.contentType,
310
+ kind: item.kind || inferBncrStructuredMediaKind(item.contentType),
311
+ messageId: input.parsed.msgId,
312
+ })),
201
313
  });
202
314
  }
@@ -15,21 +15,45 @@ export type BncrInboundParsedPeer = OpenClawChannelPeer;
15
15
 
16
16
  export type BncrInboundParamsInput = {
17
17
  accountId?: unknown;
18
+ protocolVersion?: unknown;
19
+ capabilities?: unknown;
18
20
  platform?: unknown;
19
21
  groupId?: unknown;
22
+ groupName?: unknown;
20
23
  userId?: unknown;
24
+ userName?: unknown;
21
25
  sessionKey?: unknown;
22
26
  originatingTo?: unknown;
23
27
  providedOriginatingTo?: unknown;
24
28
  to?: unknown;
25
29
  clientId?: unknown;
30
+ bridgeId?: unknown;
31
+ bridgeName?: unknown;
32
+ isGroup?: unknown;
33
+ isAdmin?: unknown;
26
34
  msg?: unknown;
27
35
  type?: unknown;
28
36
  base64?: unknown;
29
37
  path?: unknown;
38
+ paths?: unknown;
39
+ mediaList?: unknown;
30
40
  mimeType?: unknown;
31
41
  fileName?: unknown;
32
42
  msgId?: unknown;
43
+ shouldRespond?: unknown;
44
+ triggerKind?: unknown;
45
+ botName?: unknown;
46
+ isBotMentioned?: unknown;
47
+ isReplyToBot?: unknown;
48
+ };
49
+
50
+ export type BncrInboundMediaItem = {
51
+ path?: string;
52
+ base64?: string;
53
+ mimeType?: string;
54
+ fileName?: string;
55
+ type?: string;
56
+ transferId?: string;
33
57
  };
34
58
 
35
59
  export type BncrRememberSessionRoute = (