@xmoxmo/bncr 0.4.9 → 0.5.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmoxmo/bncr",
3
- "version": "0.4.9",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/channel.ts CHANGED
@@ -1551,6 +1551,7 @@ class BncrBridgeRuntime {
1551
1551
  resolvedAgentId: string;
1552
1552
  shouldDispatch: boolean;
1553
1553
  shouldAccumulate: boolean;
1554
+ dispatchBy: string;
1554
1555
  }
1555
1556
  | {
1556
1557
  ok: false;
@@ -1,3 +1,5 @@
1
+ import path from 'node:path';
2
+
1
3
  export type BncrStructuredContextFactsInput = {
2
4
  channelId: string;
3
5
  accountId: string;
@@ -54,8 +56,54 @@ export type BncrStructuredContextFactsInput = {
54
56
  kind?: string;
55
57
  messageId?: string;
56
58
  }>;
59
+ pendingMediaContext?: Array<{
60
+ messageId?: string;
61
+ sender?: string;
62
+ senderId?: string;
63
+ body?: string;
64
+ media?: Array<{
65
+ path?: string;
66
+ contentType?: string;
67
+ kind?: string;
68
+ messageId?: string;
69
+ }>;
70
+ }>;
57
71
  };
58
72
 
73
+ function toBncrPromptMediaPath(id: string): string | undefined {
74
+ if (
75
+ !id ||
76
+ id === '.' ||
77
+ id === '..' ||
78
+ id.includes('/') ||
79
+ id.includes('\\') ||
80
+ id.includes('\0')
81
+ ) {
82
+ return undefined;
83
+ }
84
+ return `media://inbound/${encodeURIComponent(id)}`;
85
+ }
86
+
87
+ function resolveBncrPromptMediaPath(mediaPath: string | undefined): string | undefined {
88
+ const normalized = String(mediaPath || '').trim();
89
+ if (!normalized) return undefined;
90
+
91
+ const canonicalMatch = /^media:\/\/inbound\/([^/\\]+)$/i.exec(normalized);
92
+ if (canonicalMatch?.[1]) {
93
+ try {
94
+ return toBncrPromptMediaPath(decodeURIComponent(canonicalMatch[1]));
95
+ } catch {
96
+ return undefined;
97
+ }
98
+ }
99
+
100
+ const slashNormalized = normalized.replace(/\\/g, '/');
101
+ if (!slashNormalized.includes('/media/inbound/')) {
102
+ return undefined;
103
+ }
104
+ return toBncrPromptMediaPath(path.posix.basename(slashNormalized));
105
+ }
106
+
59
107
  export function buildBncrStructuredContextFacts(input: BncrStructuredContextFactsInput) {
60
108
  const rawBody = input.message.rawBody;
61
109
  return {
@@ -116,6 +164,18 @@ export function buildBncrStructuredContextFacts(input: BncrStructuredContextFact
116
164
  kind: item.kind,
117
165
  messageId: item.messageId,
118
166
  })),
167
+ pendingMediaContext: (input.pendingMediaContext || []).map((entry) => ({
168
+ messageId: entry.messageId,
169
+ sender: entry.sender,
170
+ senderId: entry.senderId,
171
+ body: entry.body,
172
+ media: (entry.media || []).map((item) => ({
173
+ path: item.path,
174
+ contentType: item.contentType,
175
+ kind: item.kind,
176
+ messageId: item.messageId,
177
+ })),
178
+ })),
119
179
  };
120
180
  }
121
181
 
@@ -141,10 +201,23 @@ export function buildBncrPromptVisibleContextFacts(
141
201
  originatingTo: string;
142
202
  };
143
203
  media?: Array<{
204
+ path?: string;
144
205
  contentType?: string;
145
206
  kind?: string;
146
207
  messageId?: string;
147
208
  }>;
209
+ pendingMediaContext?: Array<{
210
+ messageId?: string;
211
+ sender?: string;
212
+ senderId?: string;
213
+ body?: string;
214
+ media?: Array<{
215
+ path?: string;
216
+ contentType?: string;
217
+ kind?: string;
218
+ messageId?: string;
219
+ }>;
220
+ }>;
148
221
  } = {};
149
222
 
150
223
  if (
@@ -179,11 +252,39 @@ export function buildBncrPromptVisibleContextFacts(
179
252
  }
180
253
 
181
254
  if (facts.media.length > 0) {
182
- result.media = facts.media.map((item) => ({
183
- contentType: item.contentType,
184
- kind: item.kind,
185
- messageId: item.messageId,
186
- }));
255
+ result.media = facts.media.flatMap((item) => {
256
+ const promptMediaPath = resolveBncrPromptMediaPath(item.path);
257
+ const payload = {
258
+ ...(promptMediaPath ? { path: promptMediaPath } : {}),
259
+ contentType: item.contentType,
260
+ kind: item.kind,
261
+ messageId: item.messageId,
262
+ };
263
+ return Object.values(payload).some((field) => field !== undefined) ? [payload] : [];
264
+ });
265
+ }
266
+
267
+ if (facts.pendingMediaContext?.length) {
268
+ result.pendingMediaContext = facts.pendingMediaContext.flatMap((entry) => {
269
+ const media = (entry.media || []).flatMap((item) => {
270
+ const promptMediaPath = resolveBncrPromptMediaPath(item.path);
271
+ const payload = {
272
+ ...(promptMediaPath ? { path: promptMediaPath } : {}),
273
+ contentType: item.contentType,
274
+ kind: item.kind,
275
+ messageId: item.messageId,
276
+ };
277
+ return Object.values(payload).some((field) => field !== undefined) ? [payload] : [];
278
+ });
279
+ const payload = {
280
+ messageId: entry.messageId,
281
+ sender: entry.sender,
282
+ senderId: entry.senderId,
283
+ body: entry.body,
284
+ ...(media.length > 0 ? { media } : {}),
285
+ };
286
+ return Object.values(payload).some((field) => field !== undefined) ? [payload] : [];
287
+ });
187
288
  }
188
289
 
189
290
  return result;
@@ -248,6 +349,18 @@ export type BncrStructuredContextFactsFromInboundPartsInput = {
248
349
  bridgeSenderName?: string;
249
350
  senderIsOwner?: boolean;
250
351
  senderIsAuthorized?: boolean;
352
+ pendingMediaContext?: Array<{
353
+ messageId?: string;
354
+ sender?: string;
355
+ senderId?: string;
356
+ body?: string;
357
+ media?: Array<{
358
+ path?: string;
359
+ contentType?: string;
360
+ kind?: string;
361
+ messageId?: string;
362
+ }>;
363
+ }>;
251
364
  };
252
365
 
253
366
  export function buildBncrStructuredContextFactsFromInboundParts(
@@ -310,5 +423,6 @@ export function buildBncrStructuredContextFactsFromInboundParts(
310
423
  kind: item.kind || inferBncrStructuredMediaKind(item.contentType),
311
424
  messageId: input.parsed.msgId,
312
425
  })),
426
+ pendingMediaContext: input.pendingMediaContext,
313
427
  });
314
428
  }
@@ -153,12 +153,14 @@ export async function dispatchBncrInbound(params: {
153
153
  historyMap: groupHistories,
154
154
  parsed,
155
155
  senderDisplayName,
156
+ senderId: senderIdForContext,
156
157
  bodyText: rawBody,
157
158
  });
158
159
  await recordBncrPendingGroupMedia({
159
160
  historyMap: groupHistories,
160
161
  parsed,
161
162
  senderDisplayName,
163
+ senderId: senderIdForContext,
162
164
  bodyText: rawBody,
163
165
  mediaItems,
164
166
  mediaContentType: mediaContentType || mimeType,
@@ -1,3 +1,4 @@
1
+ import path from 'node:path';
1
2
  import {
2
3
  createChannelHistoryWindow,
3
4
  DEFAULT_GROUP_HISTORY_LIMIT,
@@ -8,7 +9,11 @@ import { formatOpenClawAgentEnvelope } from '../../openclaw/reply-runtime.ts';
8
9
  import type { BncrInboundApi } from './contracts.ts';
9
10
  import type { ParsedInbound } from './dispatch-prep.ts';
10
11
 
11
- export type BncrGroupHistoryMap = Map<string, HistoryEntry[]>;
12
+ export type BncrHistoryEntry = HistoryEntry & {
13
+ senderId?: string;
14
+ };
15
+
16
+ export type BncrGroupHistoryMap = Map<string, BncrHistoryEntry[]>;
12
17
 
13
18
  type BncrHistoryMediaKind = NonNullable<HistoryMediaEntry['kind']>;
14
19
 
@@ -30,6 +35,7 @@ export function recordBncrPendingGroupText(args: {
30
35
  historyMap: BncrGroupHistoryMap;
31
36
  parsed: ParsedInbound;
32
37
  senderDisplayName: string;
38
+ senderId: string;
33
39
  bodyText: string;
34
40
  }) {
35
41
  const historyKey = buildBncrGroupHistoryKey(args.parsed);
@@ -40,6 +46,7 @@ export function recordBncrPendingGroupText(args: {
40
46
  limit: DEFAULT_GROUP_HISTORY_LIMIT,
41
47
  entry: {
42
48
  sender: args.senderDisplayName,
49
+ senderId: args.senderId,
43
50
  body,
44
51
  timestamp: Date.now(),
45
52
  messageId: args.parsed.msgId,
@@ -74,6 +81,7 @@ export async function recordBncrPendingGroupMedia(args: {
74
81
  historyMap: BncrGroupHistoryMap;
75
82
  parsed: ParsedInbound;
76
83
  senderDisplayName: string;
84
+ senderId: string;
77
85
  bodyText: string;
78
86
  mediaItems?: Array<{
79
87
  path: string;
@@ -101,12 +109,13 @@ export async function recordBncrPendingGroupMedia(args: {
101
109
  mediaContentType: args.mediaContentType,
102
110
  });
103
111
  const body = normalizeTextBody(args.bodyText) || buildBncrHistoryMediaBody(kind);
104
- if (normalizedMediaItems.length === 0 || kind !== 'image') {
112
+ if (normalizedMediaItems.length === 0) {
105
113
  createChannelHistoryWindow({ historyMap: args.historyMap }).record({
106
114
  historyKey,
107
115
  limit: DEFAULT_GROUP_HISTORY_LIMIT,
108
116
  entry: {
109
117
  sender: args.senderDisplayName,
118
+ senderId: args.senderId,
110
119
  body,
111
120
  timestamp: Date.now(),
112
121
  messageId: args.parsed.msgId,
@@ -114,25 +123,25 @@ export async function recordBncrPendingGroupMedia(args: {
114
123
  });
115
124
  return;
116
125
  }
117
- await createChannelHistoryWindow({ historyMap: args.historyMap }).recordWithMedia({
126
+ createChannelHistoryWindow({ historyMap: args.historyMap }).record({
118
127
  historyKey,
119
128
  limit: DEFAULT_GROUP_HISTORY_LIMIT,
120
129
  entry: {
121
130
  sender: args.senderDisplayName,
131
+ senderId: args.senderId,
122
132
  body,
123
133
  timestamp: Date.now(),
124
134
  messageId: args.parsed.msgId,
135
+ media: normalizedMediaItems.map(
136
+ (item) =>
137
+ ({
138
+ path: item.path,
139
+ contentType: item.contentType || args.mediaContentType,
140
+ kind: item.kind || kind,
141
+ messageId: args.parsed.msgId,
142
+ }) satisfies HistoryMediaEntry,
143
+ ),
125
144
  },
126
- messageId: args.parsed.msgId,
127
- media: normalizedMediaItems.map(
128
- (item) =>
129
- ({
130
- path: item.path,
131
- contentType: item.contentType || args.mediaContentType || 'image/*',
132
- kind: 'image',
133
- messageId: args.parsed.msgId,
134
- }) satisfies HistoryMediaEntry,
135
- ),
136
145
  });
137
146
  }
138
147
 
@@ -164,16 +173,91 @@ export function buildBncrPendingGroupContext(args: {
164
173
  });
165
174
  }
166
175
 
167
- export function buildBncrInboundHistory(args: {
176
+ export function collectBncrPendingHistoryMedia(args: {
168
177
  historyMap: BncrGroupHistoryMap;
169
178
  parsed: ParsedInbound;
179
+ }): HistoryMediaEntry[] {
180
+ const historyKey = buildBncrGroupHistoryKey(args.parsed);
181
+ if (!historyKey) return [];
182
+ const entries = args.historyMap.get(historyKey) || [];
183
+ return entries.flatMap((entry) => (Array.isArray(entry.media) ? entry.media : []));
184
+ }
185
+
186
+ function toBncrPromptMediaPath(value: string | undefined): string | undefined {
187
+ const normalized = String(value || '').trim();
188
+ if (!normalized) return undefined;
189
+ const slashNormalized = normalized.replace(/\\/g, '/');
190
+ if (!slashNormalized.includes('/media/inbound/')) return undefined;
191
+ const base = path.posix.basename(slashNormalized);
192
+ if (!base || base === '.' || base === '..') return undefined;
193
+ return `media://inbound/${encodeURIComponent(base)}`;
194
+ }
195
+
196
+ function buildHistoryWindowMediaSummary(
197
+ media: HistoryMediaEntry[] | undefined,
198
+ ): string | undefined {
199
+ if (!Array.isArray(media) || media.length === 0) return undefined;
200
+ const kinds = media
201
+ .map((item) =>
202
+ String(item?.kind || '')
203
+ .trim()
204
+ .toLowerCase(),
205
+ )
206
+ .filter(Boolean);
207
+ const uniformKind = kinds[0] && kinds.every((item) => item === kinds[0]) ? kinds[0] : 'document';
208
+ const count = media.length;
209
+ if (uniformKind === 'image')
210
+ return count === 1 ? '<media:image>' : `<media:image> (${count} images)`;
211
+ if (uniformKind === 'video')
212
+ return count === 1 ? '<media:video>' : `<media:video> (${count} videos)`;
213
+ if (uniformKind === 'audio') {
214
+ return count === 1 ? '<media:audio>' : `<media:audio> (${count} audio attachments)`;
215
+ }
216
+ return count === 1 ? '<media:document>' : `<media:document> (${count} attachments)`;
217
+ }
218
+
219
+ function buildHistoryWindowMedias(media: HistoryMediaEntry[] | undefined) {
220
+ if (!Array.isArray(media) || media.length === 0) return [];
221
+ return media.flatMap((item) => {
222
+ const promptPath = toBncrPromptMediaPath(item?.path);
223
+ const payload = {
224
+ ...(promptPath ? { path: promptPath } : {}),
225
+ ...(item?.contentType ? { contentType: item.contentType } : {}),
226
+ ...(item?.kind ? { kind: item.kind } : {}),
227
+ ...(item?.messageId ? { messageId: item.messageId } : {}),
228
+ };
229
+ return Object.keys(payload).length > 0 ? [payload] : [];
230
+ });
231
+ }
232
+
233
+ export function buildBncrPendingHistoryWindowContext(args: {
234
+ historyMap: BncrGroupHistoryMap;
235
+ parsed: ParsedInbound;
236
+ channelId: string;
170
237
  }) {
171
238
  const historyKey = buildBncrGroupHistoryKey(args.parsed);
172
239
  if (!historyKey) return undefined;
173
- return createChannelHistoryWindow({ historyMap: args.historyMap }).buildInboundHistory({
174
- historyKey,
175
- limit: DEFAULT_GROUP_HISTORY_LIMIT,
176
- });
240
+ const entries = args.historyMap.get(historyKey) || [];
241
+ if (entries.length === 0) return undefined;
242
+ const messages = entries.slice(-DEFAULT_GROUP_HISTORY_LIMIT).map((entry) => ({
243
+ ...(entry.messageId ? { messageId: entry.messageId } : {}),
244
+ sender: entry.sender,
245
+ senderId: entry.senderId,
246
+ ...(typeof entry.timestamp === 'number' ? { timestampMs: entry.timestamp } : {}),
247
+ body: entry.body,
248
+ mediaSummary: buildHistoryWindowMediaSummary(entry.media),
249
+ medias: buildHistoryWindowMedias(entry.media),
250
+ }));
251
+ return {
252
+ label: 'Bncr history window',
253
+ source: args.channelId,
254
+ type: 'bncr.history_window',
255
+ payload: {
256
+ relation: 'before_current_message',
257
+ order: 'chronological',
258
+ messages,
259
+ },
260
+ };
177
261
  }
178
262
 
179
263
  export function clearBncrPendingGroupHistory(args: {
@@ -12,8 +12,9 @@ import type {
12
12
  } from './dispatch-prep.ts';
13
13
  import {
14
14
  type BncrGroupHistoryMap,
15
- buildBncrInboundHistory,
16
15
  buildBncrPendingGroupContext,
16
+ buildBncrPendingHistoryWindowContext,
17
+ collectBncrPendingHistoryMedia,
17
18
  } from './group-history.ts';
18
19
  import { resolveBncrChannelInboundRuntime } from './runtime-compat.ts';
19
20
 
@@ -88,6 +89,15 @@ export function buildBncrInboundTurnContext(args: {
88
89
  } = args;
89
90
  const senderIsOwner = parsed.isAdmin === true || Boolean(ownerAllowFrom?.length);
90
91
  const senderIsAuthorized = senderIsOwner;
92
+ const inboundHistory = undefined;
93
+ const pendingHistoryMedia =
94
+ shouldDispatch && parsed.peer.kind === 'group'
95
+ ? collectBncrPendingHistoryMedia({ historyMap: groupHistories, parsed })
96
+ : [];
97
+ const pendingHistoryWindowContext =
98
+ shouldDispatch && parsed.peer.kind === 'group'
99
+ ? buildBncrPendingHistoryWindowContext({ historyMap: groupHistories, parsed, channelId })
100
+ : undefined;
91
101
  const structuredContextFacts = buildBncrStructuredContextFactsFromInboundParts({
92
102
  channelId,
93
103
  parsed,
@@ -105,16 +115,19 @@ export function buildBncrInboundTurnContext(args: {
105
115
  senderIsAuthorized,
106
116
  });
107
117
  const promptVisibleContextFacts = buildBncrPromptVisibleContextFacts(structuredContextFacts);
108
- const supplementalUntrustedContext = Object.keys(promptVisibleContextFacts).length
109
- ? [
110
- {
111
- label: 'Bncr inbound context',
112
- source: channelId,
113
- type: 'bncr.inbound_context',
114
- payload: promptVisibleContextFacts,
115
- },
116
- ]
117
- : [];
118
+ const supplementalUntrustedContext = [
119
+ ...(pendingHistoryWindowContext ? [pendingHistoryWindowContext] : []),
120
+ ...(Object.keys(promptVisibleContextFacts).length
121
+ ? [
122
+ {
123
+ label: 'Bncr inbound context',
124
+ source: channelId,
125
+ type: 'bncr.inbound_context',
126
+ payload: promptVisibleContextFacts,
127
+ },
128
+ ]
129
+ : []),
130
+ ];
118
131
  const platformWantsReply = parsed.shouldRespond === true;
119
132
  const wasMentioned = parsed.isBotMentioned === true || platformWantsReply;
120
133
  const isAuthorizedTextCommand = Boolean(ownerAllowFrom?.length);
@@ -134,10 +147,13 @@ export function buildBncrInboundTurnContext(args: {
134
147
  currentMessage: prepared.body,
135
148
  })
136
149
  : prepared.body;
137
- const inboundHistory =
138
- shouldDispatch && parsed.peer.kind === 'group'
139
- ? buildBncrInboundHistory({ historyMap: groupHistories, parsed })
140
- : undefined;
150
+ const currentTurnMedia = prepared.mediaItems.map((item) => ({
151
+ path: item.path,
152
+ contentType: item.contentType,
153
+ kind: item.kind,
154
+ messageId: msgId ?? undefined,
155
+ }));
156
+ const media = [...pendingHistoryMedia, ...currentTurnMedia];
141
157
 
142
158
  return Promise.resolve(
143
159
  resolveBncrChannelInboundRuntime(api).buildContext({
@@ -194,15 +210,7 @@ export function buildBncrInboundTurnContext(args: {
194
210
  },
195
211
  }
196
212
  : {}),
197
- media:
198
- prepared.mediaItems.length > 0
199
- ? prepared.mediaItems.map((item) => ({
200
- path: item.path,
201
- contentType: item.contentType,
202
- kind: item.kind,
203
- messageId: msgId ?? undefined,
204
- }))
205
- : [],
213
+ media,
206
214
  access: {
207
215
  mentions: {
208
216
  canDetectMention: true,
@@ -478,6 +478,7 @@ export function buildBncrInboundSurfaceRuntime(deps: {
478
478
  resolvedAgentId: string;
479
479
  shouldDispatch: boolean;
480
480
  shouldAccumulate: boolean;
481
+ dispatchBy: string;
481
482
  }
482
483
  | {
483
484
  ok: false;
@@ -11,6 +11,34 @@ import { decideSceneAdmission } from './scene-registry.ts';
11
11
 
12
12
  type InboundAcceptanceResponsePayload = ReturnType<typeof buildInboundResponsePayload>;
13
13
 
14
+ function resolveDispatchBy(args: {
15
+ parsed: ReturnType<typeof parseBncrInboundParams>;
16
+ admission: ReturnType<typeof decideSceneAdmission>;
17
+ cfg: BncrChannelConfigRoot;
18
+ }): string {
19
+ const { parsed, admission, cfg } = args;
20
+ if (parsed.peer.kind === 'direct') return 'direct';
21
+ if (!admission.allowed) return 'denied';
22
+
23
+ const isBncrNativeCommand =
24
+ parseBncrNativeCommand(parsed.extracted.text, {
25
+ allowBareWhoami: parsed.isAdmin !== true,
26
+ }) !== null;
27
+ const isAdminOpenClawNativeCommand =
28
+ parsed.isAdmin === true &&
29
+ !isBncrNativeCommand &&
30
+ hasControlCommand(parsed.extracted.text, cfg as Parameters<typeof hasControlCommand>[1]);
31
+ if (isAdminOpenClawNativeCommand) return 'native-command-admin';
32
+
33
+ const mode = admission.scene.groupReplyMode || 'admin';
34
+ if (mode === 'all') return 'mode-all';
35
+ if (mode === 'admin') return parsed.isAdmin === true ? 'mode-admin-admin' : 'mode-admin-blocked';
36
+ if (mode === 'mention')
37
+ return parsed.shouldRespond === true ? 'mode-mention-trigger' : 'mode-mention-idle';
38
+ if (parsed.isAdmin === true) return 'mode-hybrid-admin';
39
+ return parsed.shouldRespond === true ? 'mode-hybrid-trigger' : 'mode-hybrid-idle';
40
+ }
41
+
14
42
  function shouldDispatchForScene(args: {
15
43
  parsed: ReturnType<typeof parseBncrInboundParams>;
16
44
  admission: ReturnType<typeof decideSceneAdmission>;
@@ -184,5 +212,6 @@ export async function prepareBncrInboundAcceptance(args: {
184
212
  resolvedAgentId: admission.agentId,
185
213
  shouldDispatch: shouldDispatchForScene({ parsed, admission, cfg }),
186
214
  shouldAccumulate: shouldAccumulateForScene({ parsed, admission, cfg }),
215
+ dispatchBy: resolveDispatchBy({ parsed, admission, cfg }),
187
216
  };
188
217
  }
@@ -45,6 +45,7 @@ type InboundAcceptanceResult =
45
45
  resolvedAgentId: string;
46
46
  shouldDispatch: boolean;
47
47
  shouldAccumulate: boolean;
48
+ dispatchBy: string;
48
49
  }
49
50
  | {
50
51
  ok: false;
@@ -254,7 +255,7 @@ export function createBncrInboundHandlers(runtime: Omit<BncrInboundHandlersRunti
254
255
  .trim();
255
256
  runtime.logInfo(
256
257
  'inbound',
257
- `accept reject|accountId=${accountId}|msgId=${msgId ?? '-'}|scope=${runtime.formatDisplayScope(route)}|chatType=${peer.kind}|msgType=${msgType}|status=${acceptance.status}|reason=${reason || '-'}|isAdmin=${parsed.isAdmin === true}|shouldRespond=${parsed.shouldRespond === true}`,
258
+ `accept reject|accountId=${accountId}|msgId=${msgId ?? '-'}|scope=${runtime.formatDisplayScope(route)}|chatType=${peer.kind}|msgType=${msgType}|status=${acceptance.status}|reason=${reason || '-'}|isAdmin=${parsed.isAdmin === true}|platformShouldRespond=${parsed.shouldRespond === true}`,
258
259
  );
259
260
  const pendingNotice = buildBncrPendingDirectApprovalNotice({ parsed, reason });
260
261
  if (pendingNotice) {
@@ -280,10 +281,11 @@ export function createBncrInboundHandlers(runtime: Omit<BncrInboundHandlersRunti
280
281
  resolvedAgentId,
281
282
  shouldDispatch,
282
283
  shouldAccumulate,
284
+ dispatchBy,
283
285
  } = acceptance;
284
286
  runtime.logInfo(
285
287
  'inbound',
286
- `accept ok|accountId=${accountId}|msgId=${msgId ?? '-'}|scope=${runtime.formatDisplayScope(route)}|chatType=${peer.kind}|msgType=${msgType}|sessionKey=${sessionKey}|agent=${resolvedAgentId}|dispatch=${shouldDispatch}|accumulate=${shouldAccumulate}|isAdmin=${parsed.isAdmin === true}|shouldRespond=${parsed.shouldRespond === true}`,
288
+ `accept ok|accountId=${accountId}|msgId=${msgId ?? '-'}|scope=${runtime.formatDisplayScope(route)}|chatType=${peer.kind}|msgType=${msgType}|sessionKey=${sessionKey}|agent=${resolvedAgentId}|dispatch=${shouldDispatch}|dispatchBy=${dispatchBy}|accumulate=${shouldAccumulate}|isAdmin=${parsed.isAdmin === true}|platformShouldRespond=${parsed.shouldRespond === true}`,
287
289
  );
288
290
  runtime.logInfo(
289
291
  'inbound',
@@ -7,4 +7,5 @@ export const BNCR_CHANNEL_META = {
7
7
  docsPath: '/channels/bncr',
8
8
  blurb: 'Bncr Channel.',
9
9
  aliases: ['bncr'],
10
+ preferSessionLookupForAnnounceTarget: true,
10
11
  };