@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.
- package/dist/index.js +6 -0
- package/index.ts +6 -0
- package/package.json +1 -1
- package/src/channel.ts +41 -2
- package/src/core/targets.ts +106 -17
- package/src/messaging/inbound/commands.ts +263 -51
- package/src/messaging/inbound/context-facts.ts +126 -14
- package/src/messaging/inbound/contracts.ts +24 -0
- package/src/messaging/inbound/dispatch-prep.ts +214 -39
- package/src/messaging/inbound/dispatch.ts +71 -5
- package/src/messaging/inbound/gate.ts +56 -86
- package/src/messaging/inbound/group-history.ts +189 -0
- package/src/messaging/inbound/native-command-runtime.ts +77 -61
- package/src/messaging/inbound/native-command.ts +92 -8
- package/src/messaging/inbound/parse.ts +113 -8
- package/src/messaging/inbound/reply-dispatch-serial.ts +62 -0
- package/src/messaging/inbound/reply-dispatch.ts +252 -77
- package/src/messaging/inbound/scene-admin.ts +269 -0
- package/src/messaging/inbound/session-label.ts +122 -13
- package/src/messaging/inbound/session-meta-task.ts +17 -0
- package/src/messaging/inbound/turn-context.ts +184 -71
- package/src/openclaw/channel-runtime-contracts.ts +1 -0
- package/src/plugin/channel-components.ts +34 -1
- package/src/plugin/channel-inbound-helpers.ts +9 -2
- package/src/plugin/channel-runtime-builders-delivery.ts +24 -1
- package/src/plugin/channel-runtime-types.ts +42 -0
- package/src/plugin/file-inbound-init.ts +27 -12
- package/src/plugin/file-inbound-runtime.ts +2 -0
- package/src/plugin/inbound-acceptance.ts +82 -1
- package/src/plugin/inbound-handlers.ts +55 -2
- package/src/plugin/inbound-surface-handlers-group.ts +16 -0
- package/src/plugin/messaging.ts +22 -5
- package/src/plugin/scene-registry.ts +155 -0
- package/src/plugin/state-store.ts +133 -0
- package/src/plugin/state-transient-runtime-group.ts +5 -0
- package/src/plugin/target-runtime.ts +2 -2
|
@@ -1,41 +1,93 @@
|
|
|
1
|
+
import { readBncrSessionUpdatedAt } from '../../openclaw/inbound-session-runtime.ts';
|
|
2
|
+
import { resolveOpenClawEnvelopeFormatOptions } from '../../openclaw/reply-runtime.ts';
|
|
1
3
|
import {
|
|
2
4
|
buildBncrPromptVisibleContextFacts,
|
|
3
5
|
buildBncrStructuredContextFactsFromInboundParts,
|
|
4
6
|
} from './context-facts.ts';
|
|
5
|
-
import type { BncrInboundApi, BncrInboundContextPayload } from './contracts.ts';
|
|
7
|
+
import type { BncrInboundApi, BncrInboundConfig, BncrInboundContextPayload } from './contracts.ts';
|
|
6
8
|
import type {
|
|
7
9
|
BncrInboundConversationResolution,
|
|
8
10
|
BncrPreparedInboundSessionContext,
|
|
9
11
|
ParsedInbound,
|
|
10
12
|
} from './dispatch-prep.ts';
|
|
13
|
+
import {
|
|
14
|
+
type BncrGroupHistoryMap,
|
|
15
|
+
buildBncrInboundHistory,
|
|
16
|
+
buildBncrPendingGroupContext,
|
|
17
|
+
} from './group-history.ts';
|
|
11
18
|
import { resolveBncrChannelInboundRuntime } from './runtime-compat.ts';
|
|
12
19
|
|
|
20
|
+
function parseSlashCommandName(body: string): string | undefined {
|
|
21
|
+
const raw = String(body || '').trim();
|
|
22
|
+
if (!raw.startsWith('/')) return undefined;
|
|
23
|
+
return raw.slice(1).split(/\s+/, 1)[0]?.split('@', 1)[0] || undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function applyBncrLegacyCommandFields(args: {
|
|
27
|
+
ctx: BncrInboundContextPayload;
|
|
28
|
+
channelId: string;
|
|
29
|
+
senderIdForContext: string;
|
|
30
|
+
resolution: BncrInboundConversationResolution;
|
|
31
|
+
rawBody: string;
|
|
32
|
+
ownerAllowFrom?: string[];
|
|
33
|
+
isAuthorizedTextCommand: boolean;
|
|
34
|
+
}) {
|
|
35
|
+
const { ctx, channelId, senderIdForContext, resolution, rawBody, ownerAllowFrom } = args;
|
|
36
|
+
ctx.From = senderIdForContext;
|
|
37
|
+
ctx.To = resolution.canonicalTo;
|
|
38
|
+
ctx.SenderId = senderIdForContext;
|
|
39
|
+
ctx.OriginatingChannel = channelId;
|
|
40
|
+
if (ownerAllowFrom?.length) ctx.OwnerAllowFrom = ownerAllowFrom;
|
|
41
|
+
if (args.isAuthorizedTextCommand) {
|
|
42
|
+
ctx.CommandAuthorized = true;
|
|
43
|
+
ctx.CommandSource = 'text';
|
|
44
|
+
ctx.CommandTurn = {
|
|
45
|
+
kind: 'text-slash',
|
|
46
|
+
source: 'text',
|
|
47
|
+
authorized: true,
|
|
48
|
+
commandName: parseSlashCommandName(rawBody),
|
|
49
|
+
body: rawBody,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return ctx;
|
|
53
|
+
}
|
|
54
|
+
|
|
13
55
|
export function buildBncrInboundTurnContext(args: {
|
|
14
56
|
api: BncrInboundApi;
|
|
57
|
+
cfg: BncrInboundConfig;
|
|
15
58
|
channelId: string;
|
|
16
59
|
parsed: ParsedInbound;
|
|
17
60
|
msgId?: string | null;
|
|
18
|
-
mimeType?: string;
|
|
19
|
-
mediaPath?: string;
|
|
20
61
|
peer: ParsedInbound['peer'];
|
|
21
62
|
senderIdForContext: string;
|
|
22
63
|
senderDisplayName: string;
|
|
64
|
+
ownerAllowFrom?: string[];
|
|
65
|
+
bridgeSenderId?: string;
|
|
66
|
+
bridgeSenderName?: string;
|
|
23
67
|
resolution: BncrInboundConversationResolution;
|
|
24
68
|
prepared: BncrPreparedInboundSessionContext;
|
|
69
|
+
groupHistories: BncrGroupHistoryMap;
|
|
70
|
+
shouldDispatch: boolean;
|
|
25
71
|
}): BncrInboundContextPayload | Promise<BncrInboundContextPayload> {
|
|
26
72
|
const {
|
|
27
73
|
api,
|
|
74
|
+
cfg,
|
|
28
75
|
channelId,
|
|
29
76
|
parsed,
|
|
30
77
|
msgId,
|
|
31
|
-
mimeType,
|
|
32
|
-
mediaPath,
|
|
33
78
|
peer,
|
|
34
79
|
senderIdForContext,
|
|
35
80
|
senderDisplayName,
|
|
81
|
+
ownerAllowFrom,
|
|
82
|
+
bridgeSenderId,
|
|
83
|
+
bridgeSenderName,
|
|
36
84
|
resolution,
|
|
37
85
|
prepared,
|
|
86
|
+
groupHistories,
|
|
87
|
+
shouldDispatch,
|
|
38
88
|
} = args;
|
|
89
|
+
const senderIsOwner = parsed.isAdmin === true || Boolean(ownerAllowFrom?.length);
|
|
90
|
+
const senderIsAuthorized = senderIsOwner;
|
|
39
91
|
const structuredContextFacts = buildBncrStructuredContextFactsFromInboundParts({
|
|
40
92
|
channelId,
|
|
41
93
|
parsed,
|
|
@@ -43,11 +95,14 @@ export function buildBncrInboundTurnContext(args: {
|
|
|
43
95
|
prepared: {
|
|
44
96
|
rawBody: prepared.rawBody,
|
|
45
97
|
body: prepared.body,
|
|
46
|
-
|
|
47
|
-
mediaContentType: mimeType,
|
|
98
|
+
mediaItems: prepared.mediaItems,
|
|
48
99
|
},
|
|
49
100
|
senderIdForContext,
|
|
50
101
|
senderDisplayName,
|
|
102
|
+
bridgeSenderId,
|
|
103
|
+
bridgeSenderName,
|
|
104
|
+
senderIsOwner,
|
|
105
|
+
senderIsAuthorized,
|
|
51
106
|
});
|
|
52
107
|
const promptVisibleContextFacts = buildBncrPromptVisibleContextFacts(structuredContextFacts);
|
|
53
108
|
const supplementalUntrustedContext = Object.keys(promptVisibleContextFacts).length
|
|
@@ -60,72 +115,130 @@ export function buildBncrInboundTurnContext(args: {
|
|
|
60
115
|
},
|
|
61
116
|
]
|
|
62
117
|
: [];
|
|
118
|
+
const platformWantsReply = parsed.shouldRespond === true;
|
|
119
|
+
const wasMentioned = parsed.isBotMentioned === true || platformWantsReply;
|
|
120
|
+
const isAuthorizedTextCommand = Boolean(ownerAllowFrom?.length);
|
|
121
|
+
const bodyForAgent =
|
|
122
|
+
shouldDispatch && parsed.peer.kind === 'group'
|
|
123
|
+
? buildBncrPendingGroupContext({
|
|
124
|
+
api,
|
|
125
|
+
historyMap: groupHistories,
|
|
126
|
+
parsed,
|
|
127
|
+
channelLabel: resolution.canonicalTo,
|
|
128
|
+
currentTimestamp: Date.now(),
|
|
129
|
+
previousTimestamp: readBncrSessionUpdatedAt(api, {
|
|
130
|
+
storePath: prepared.storePath,
|
|
131
|
+
sessionKey: resolution.dispatchSessionKey,
|
|
132
|
+
}),
|
|
133
|
+
envelope: resolveOpenClawEnvelopeFormatOptions(api, cfg),
|
|
134
|
+
currentMessage: prepared.body,
|
|
135
|
+
})
|
|
136
|
+
: prepared.body;
|
|
137
|
+
const inboundHistory =
|
|
138
|
+
shouldDispatch && parsed.peer.kind === 'group'
|
|
139
|
+
? buildBncrInboundHistory({ historyMap: groupHistories, parsed })
|
|
140
|
+
: undefined;
|
|
63
141
|
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
label: resolution.canonicalTo,
|
|
81
|
-
routePeer: {
|
|
82
|
-
kind: peer.kind,
|
|
142
|
+
return Promise.resolve(
|
|
143
|
+
resolveBncrChannelInboundRuntime(api).buildContext({
|
|
144
|
+
channel: channelId,
|
|
145
|
+
provider: channelId,
|
|
146
|
+
surface: channelId,
|
|
147
|
+
accountId: resolution.accountId,
|
|
148
|
+
messageId: msgId,
|
|
149
|
+
timestamp: Date.now(),
|
|
150
|
+
from: senderIdForContext,
|
|
151
|
+
sender: {
|
|
152
|
+
id: senderIdForContext,
|
|
153
|
+
name: senderDisplayName,
|
|
154
|
+
username: parsed.userName || senderDisplayName,
|
|
155
|
+
},
|
|
156
|
+
conversation: {
|
|
157
|
+
kind: resolution.chatType,
|
|
83
158
|
id: peer.id,
|
|
159
|
+
label: resolution.canonicalTo,
|
|
160
|
+
routePeer: {
|
|
161
|
+
kind: peer.kind,
|
|
162
|
+
id: peer.id,
|
|
163
|
+
},
|
|
84
164
|
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
165
|
+
route: {
|
|
166
|
+
agentId: resolution.resolvedRoute.agentId,
|
|
167
|
+
accountId: resolution.accountId,
|
|
168
|
+
routeSessionKey: resolution.resolvedRoute.sessionKey,
|
|
169
|
+
dispatchSessionKey: resolution.dispatchSessionKey,
|
|
170
|
+
mainSessionKey: resolution.resolvedRoute.mainSessionKey,
|
|
171
|
+
},
|
|
172
|
+
reply: {
|
|
173
|
+
to: resolution.canonicalTo,
|
|
174
|
+
originatingTo: resolution.originatingTo,
|
|
175
|
+
},
|
|
176
|
+
message: {
|
|
177
|
+
inboundEventKind: 'user_request',
|
|
178
|
+
body: prepared.body,
|
|
179
|
+
rawBody: prepared.rawBody,
|
|
180
|
+
bodyForAgent,
|
|
181
|
+
inboundHistory,
|
|
182
|
+
commandBody: prepared.rawBody,
|
|
183
|
+
envelopeFrom: resolution.originatingTo,
|
|
184
|
+
senderLabel: senderDisplayName,
|
|
185
|
+
},
|
|
186
|
+
...(isAuthorizedTextCommand
|
|
187
|
+
? {
|
|
188
|
+
commandTurn: {
|
|
189
|
+
kind: 'text-slash' as const,
|
|
190
|
+
source: 'text' as const,
|
|
191
|
+
authorized: true,
|
|
192
|
+
commandName: parseSlashCommandName(prepared.rawBody),
|
|
193
|
+
body: prepared.rawBody,
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
: {}),
|
|
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
|
+
: [],
|
|
206
|
+
access: {
|
|
207
|
+
mentions: {
|
|
208
|
+
canDetectMention: true,
|
|
209
|
+
wasMentioned,
|
|
210
|
+
effectiveWasMentioned: wasMentioned,
|
|
211
|
+
},
|
|
212
|
+
...(isAuthorizedTextCommand
|
|
213
|
+
? {
|
|
214
|
+
commands: {
|
|
215
|
+
authorized: true,
|
|
216
|
+
allowTextCommands: true,
|
|
217
|
+
useAccessGroups: false,
|
|
218
|
+
authorizers: [],
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
: {}),
|
|
222
|
+
},
|
|
223
|
+
supplemental: {
|
|
224
|
+
untrustedContext: supplementalUntrustedContext,
|
|
225
|
+
},
|
|
226
|
+
extra: {
|
|
227
|
+
OriginatingChannel: channelId,
|
|
228
|
+
...(ownerAllowFrom?.length ? { OwnerAllowFrom: ownerAllowFrom } : {}),
|
|
229
|
+
BncrStructuredContextFacts: structuredContextFacts,
|
|
230
|
+
StructuredContextFacts: structuredContextFacts,
|
|
231
|
+
},
|
|
232
|
+
}),
|
|
233
|
+
).then((ctx) =>
|
|
234
|
+
applyBncrLegacyCommandFields({
|
|
235
|
+
ctx,
|
|
236
|
+
channelId,
|
|
237
|
+
senderIdForContext,
|
|
238
|
+
resolution,
|
|
100
239
|
rawBody: prepared.rawBody,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
},
|
|
106
|
-
media: mediaPath
|
|
107
|
-
? [
|
|
108
|
-
{
|
|
109
|
-
path: mediaPath,
|
|
110
|
-
contentType: mimeType,
|
|
111
|
-
kind: mimeType?.startsWith('image/')
|
|
112
|
-
? 'image'
|
|
113
|
-
: mimeType?.startsWith('video/')
|
|
114
|
-
? 'video'
|
|
115
|
-
: mimeType?.startsWith('audio/')
|
|
116
|
-
? 'audio'
|
|
117
|
-
: 'document',
|
|
118
|
-
messageId: msgId ?? undefined,
|
|
119
|
-
},
|
|
120
|
-
]
|
|
121
|
-
: [],
|
|
122
|
-
supplemental: {
|
|
123
|
-
untrustedContext: supplementalUntrustedContext,
|
|
124
|
-
},
|
|
125
|
-
extra: {
|
|
126
|
-
OriginatingChannel: channelId,
|
|
127
|
-
BncrStructuredContextFacts: structuredContextFacts,
|
|
128
|
-
StructuredContextFacts: structuredContextFacts,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
240
|
+
ownerAllowFrom,
|
|
241
|
+
isAuthorizedTextCommand,
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
131
244
|
}
|
|
@@ -5,6 +5,7 @@ import { formatDisplayScope, normalizeStoredSessionKey, parseRouteLike } from '.
|
|
|
5
5
|
import type { BncrRoute } from '../core/types.ts';
|
|
6
6
|
import type { BncrInboundParamsInput } from '../messaging/inbound/contracts.ts';
|
|
7
7
|
import { dispatchBncrInbound } from '../messaging/inbound/dispatch.ts';
|
|
8
|
+
import type { BncrGroupHistoryMap } from '../messaging/inbound/group-history.ts';
|
|
8
9
|
import { parseBncrInboundParams } from '../messaging/inbound/parse.ts';
|
|
9
10
|
import { OUTBOUND_FLUSH_REASON, OUTBOUND_FLUSH_TRIGGER } from '../messaging/outbound/reasons.ts';
|
|
10
11
|
import type { ReplyPayloadInput } from '../messaging/outbound/reply-enqueue.ts';
|
|
@@ -150,6 +151,9 @@ export function createBncrFileInboundHandlersComponent(runtime: {
|
|
|
150
151
|
typeof createBncrFileInboundHandlers
|
|
151
152
|
>[0]['refreshAcceptedFileTransferLiveState'];
|
|
152
153
|
logWarn: Parameters<typeof createBncrFileInboundHandlers>[0]['logWarn'];
|
|
154
|
+
buildCanonicalSessionKey: Parameters<
|
|
155
|
+
typeof createBncrFileInboundHandlers
|
|
156
|
+
>[0]['buildCanonicalSessionKey'];
|
|
153
157
|
fileRecvTransfers: Parameters<typeof createBncrFileInboundHandlers>[0]['fileRecvTransfers'];
|
|
154
158
|
inboundFileTransferMaxBytes: number;
|
|
155
159
|
inboundFileTransferMaxChunks: number;
|
|
@@ -166,6 +170,7 @@ export function createBncrFileInboundHandlersComponent(runtime: {
|
|
|
166
170
|
logWarn: runtime.logWarn,
|
|
167
171
|
parseRouteLike,
|
|
168
172
|
normalizeStoredSessionKey,
|
|
173
|
+
buildCanonicalSessionKey: runtime.buildCanonicalSessionKey,
|
|
169
174
|
saveInboundMediaBuffer: async ({ buffer, mimeType, fileName }) =>
|
|
170
175
|
await saveOpenClawChannelMediaBuffer(
|
|
171
176
|
runtime.getApi(),
|
|
@@ -209,6 +214,10 @@ export function createBncrInboundHandlersComponent(runtime: {
|
|
|
209
214
|
>[0]['buildActiveConnectionDebugList'];
|
|
210
215
|
markLastInboundAt: (accountId: string) => void;
|
|
211
216
|
ensureCanonicalAgentId: Parameters<typeof createBncrInboundHandlers>[0]['ensureCanonicalAgentId'];
|
|
217
|
+
defaultAdminAgentId: Parameters<typeof createBncrInboundHandlers>[0]['defaultAdminAgentId'];
|
|
218
|
+
defaultPublicAgentId: Parameters<typeof createBncrInboundHandlers>[0]['defaultPublicAgentId'];
|
|
219
|
+
sceneRegistry: Parameters<typeof createBncrInboundHandlers>[0]['sceneRegistry'];
|
|
220
|
+
groupHistories: BncrGroupHistoryMap;
|
|
212
221
|
prepareInboundAcceptance: Parameters<
|
|
213
222
|
typeof createBncrInboundHandlers
|
|
214
223
|
>[0]['prepareInboundAcceptance'];
|
|
@@ -250,6 +259,10 @@ export function createBncrInboundHandlersComponent(runtime: {
|
|
|
250
259
|
markLastInboundAt: runtime.markLastInboundAt,
|
|
251
260
|
getConfig: () => getOpenClawRuntimeConfig(runtime.getApi()),
|
|
252
261
|
ensureCanonicalAgentId: runtime.ensureCanonicalAgentId,
|
|
262
|
+
defaultAdminAgentId: runtime.defaultAdminAgentId,
|
|
263
|
+
defaultPublicAgentId: runtime.defaultPublicAgentId,
|
|
264
|
+
sceneRegistry: runtime.sceneRegistry,
|
|
265
|
+
groupHistories: runtime.groupHistories,
|
|
253
266
|
prepareInboundAcceptance: runtime.prepareInboundAcceptance,
|
|
254
267
|
formatDisplayScope,
|
|
255
268
|
logInboundSummary: runtime.logInboundSummary,
|
|
@@ -259,13 +272,33 @@ export function createBncrInboundHandlersComponent(runtime: {
|
|
|
259
272
|
trigger: OUTBOUND_FLUSH_TRIGGER.INBOUND,
|
|
260
273
|
reason: OUTBOUND_FLUSH_REASON.INBOUND_ACCEPTED,
|
|
261
274
|
}),
|
|
262
|
-
dispatchInbound: ({
|
|
275
|
+
dispatchInbound: ({
|
|
276
|
+
cfg,
|
|
277
|
+
parsed,
|
|
278
|
+
canonicalAgentId,
|
|
279
|
+
resolvedAgentId,
|
|
280
|
+
shouldDispatch,
|
|
281
|
+
shouldAccumulate,
|
|
282
|
+
sceneRegistry,
|
|
283
|
+
groupHistories,
|
|
284
|
+
defaultAdminAgentId,
|
|
285
|
+
defaultPublicAgentId,
|
|
286
|
+
now,
|
|
287
|
+
}) =>
|
|
263
288
|
dispatchBncrInbound({
|
|
264
289
|
api: runtime.getApi(),
|
|
265
290
|
channelId: runtime.channelId,
|
|
266
291
|
cfg,
|
|
267
292
|
parsed,
|
|
268
293
|
canonicalAgentId,
|
|
294
|
+
resolvedAgentId,
|
|
295
|
+
shouldDispatch,
|
|
296
|
+
shouldAccumulate,
|
|
297
|
+
sceneRegistry,
|
|
298
|
+
groupHistories,
|
|
299
|
+
defaultAdminAgentId,
|
|
300
|
+
defaultPublicAgentId,
|
|
301
|
+
now,
|
|
269
302
|
rememberSessionRoute: runtime.rememberSessionRoute,
|
|
270
303
|
enqueueFromReply: runtime.enqueueFromReply,
|
|
271
304
|
setInboundActivity: runtime.setInboundActivity,
|
|
@@ -55,6 +55,7 @@ export function resolveInboundSessionContext(args: {
|
|
|
55
55
|
route: BncrRoute;
|
|
56
56
|
sessionKeyFromRoute?: string;
|
|
57
57
|
canonicalAgentId: string;
|
|
58
|
+
resolvedAgentId?: string;
|
|
58
59
|
taskKey?: string;
|
|
59
60
|
text: string;
|
|
60
61
|
extractedText?: string;
|
|
@@ -74,15 +75,21 @@ export function resolveInboundSessionContext(args: {
|
|
|
74
75
|
peer: args.peer,
|
|
75
76
|
}),
|
|
76
77
|
);
|
|
78
|
+
const normalizedAgentId =
|
|
79
|
+
(args.resolvedAgentId || '').trim() || resolvedRoute.agentId || args.canonicalAgentId;
|
|
77
80
|
const baseSessionKey =
|
|
78
81
|
normalizeInboundSessionKey(
|
|
79
82
|
args.sessionKeyFromRoute || '',
|
|
80
83
|
args.route,
|
|
81
|
-
|
|
84
|
+
normalizedAgentId || '',
|
|
82
85
|
) || resolvedRoute.sessionKey;
|
|
83
86
|
const taskSessionKey = withTaskSessionKey(baseSessionKey, args.taskKey);
|
|
84
87
|
return {
|
|
85
|
-
resolvedRoute
|
|
88
|
+
resolvedRoute: {
|
|
89
|
+
...resolvedRoute,
|
|
90
|
+
agentId: normalizedAgentId || resolvedRoute.agentId,
|
|
91
|
+
sessionKey: baseSessionKey || resolvedRoute.sessionKey,
|
|
92
|
+
},
|
|
86
93
|
baseSessionKey,
|
|
87
94
|
taskSessionKey,
|
|
88
95
|
sessionKey: taskSessionKey || baseSessionKey,
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
FileSendTransferState,
|
|
8
8
|
OutboxEntry,
|
|
9
9
|
} from '../core/types.ts';
|
|
10
|
+
import type { BncrGroupHistoryMap } from '../messaging/inbound/group-history.ts';
|
|
10
11
|
import type { parseBncrInboundParams } from '../messaging/inbound/parse.ts';
|
|
11
12
|
import type {
|
|
12
13
|
NormalizedReplyPayload,
|
|
@@ -18,7 +19,11 @@ import type {
|
|
|
18
19
|
buildInboundAcceptedLifecycleDebugInfo as buildInboundAcceptedLifecycleDebugInfoFromRuntime,
|
|
19
20
|
buildInboundResponsePayload as buildInboundResponsePayloadFromRuntime,
|
|
20
21
|
} from './channel-inbound-helpers.ts';
|
|
21
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
BncrChannelConfigRoot,
|
|
24
|
+
BncrSceneRecord,
|
|
25
|
+
BncrVerifiedTarget,
|
|
26
|
+
} from './channel-runtime-types.ts';
|
|
22
27
|
import type { LeaseEventPayload } from './connection-handlers.ts';
|
|
23
28
|
import type { BncrActiveConnectionDebugEntry } from './connection-state.ts';
|
|
24
29
|
import type { FileAckPayloadState, FileAckWaiter } from './file-ack-runtime.ts';
|
|
@@ -121,6 +126,11 @@ export function buildBncrStateTransientRuntime(deps: {
|
|
|
121
126
|
maxDeadLetterEntries: number;
|
|
122
127
|
maxSessionRouteEntries: number;
|
|
123
128
|
maxAccountActivityEntries: number;
|
|
129
|
+
sceneRegistry: Map<string, BncrSceneRecord>;
|
|
130
|
+
groupHistories: Map<
|
|
131
|
+
string,
|
|
132
|
+
import('./channel-runtime-types.ts').BncrPersistedGroupHistoryEntry[]
|
|
133
|
+
>;
|
|
124
134
|
outbox: Map<string, OutboxEntry>;
|
|
125
135
|
getDeadLetter: () => OutboxEntry[];
|
|
126
136
|
setDeadLetter: (entries: OutboxEntry[]) => void;
|
|
@@ -446,6 +456,15 @@ export function buildBncrInboundSurfaceRuntime(deps: {
|
|
|
446
456
|
peer?: unknown;
|
|
447
457
|
channelId?: string;
|
|
448
458
|
}) => string;
|
|
459
|
+
defaultAdminAgentId: (args: {
|
|
460
|
+
cfg: BncrChannelConfigRoot;
|
|
461
|
+
accountId: string;
|
|
462
|
+
peer?: unknown;
|
|
463
|
+
channelId?: string;
|
|
464
|
+
}) => string;
|
|
465
|
+
defaultPublicAgentId: () => string;
|
|
466
|
+
sceneRegistry: Map<string, BncrSceneRecord>;
|
|
467
|
+
groupHistories: BncrGroupHistoryMap;
|
|
449
468
|
prepareInboundAcceptance: (args: {
|
|
450
469
|
parsed: ReturnType<typeof parseBncrInboundParams>;
|
|
451
470
|
canonicalAgentId: string;
|
|
@@ -456,6 +475,9 @@ export function buildBncrInboundSurfaceRuntime(deps: {
|
|
|
456
475
|
sessionKey: string;
|
|
457
476
|
inboundText: string;
|
|
458
477
|
hasMedia: boolean;
|
|
478
|
+
resolvedAgentId: string;
|
|
479
|
+
shouldDispatch: boolean;
|
|
480
|
+
shouldAccumulate: boolean;
|
|
459
481
|
}
|
|
460
482
|
| {
|
|
461
483
|
ok: false;
|
|
@@ -486,6 +508,7 @@ export function buildBncrInboundSurfaceRuntime(deps: {
|
|
|
486
508
|
}) => Promise<void>;
|
|
487
509
|
setInboundActivity: (accountId: string, at: number) => void;
|
|
488
510
|
scheduleSave: () => void;
|
|
511
|
+
buildCanonicalSessionKey: (route: BncrRoute) => string;
|
|
489
512
|
fileRecvTransfers: Parameters<typeof createBncrFileInboundHandlers>[0]['fileRecvTransfers'];
|
|
490
513
|
inboundFileTransferMaxBytes: number;
|
|
491
514
|
inboundFileTransferMaxChunks: number;
|
|
@@ -44,6 +44,46 @@ export type BncrPersistedLastSession = {
|
|
|
44
44
|
updatedAt: number;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
export type BncrSceneKind = 'direct' | 'group';
|
|
48
|
+
|
|
49
|
+
export type BncrSceneStatus = 'pending' | 'allowed' | 'denied';
|
|
50
|
+
|
|
51
|
+
export type BncrGroupReplyMode = 'admin' | 'mention' | 'hybrid' | 'all';
|
|
52
|
+
|
|
53
|
+
export type BncrSceneRecord = {
|
|
54
|
+
sceneKey: string;
|
|
55
|
+
kind: BncrSceneKind;
|
|
56
|
+
status: BncrSceneStatus;
|
|
57
|
+
platform: string;
|
|
58
|
+
userId?: string;
|
|
59
|
+
userName?: string;
|
|
60
|
+
groupId?: string;
|
|
61
|
+
groupName?: string;
|
|
62
|
+
agentId?: string;
|
|
63
|
+
groupReplyMode?: BncrGroupReplyMode;
|
|
64
|
+
lastSeenAt: number;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type BncrPersistedGroupHistoryEntry = {
|
|
68
|
+
sender: string;
|
|
69
|
+
body: string;
|
|
70
|
+
timestamp?: number;
|
|
71
|
+
messageId?: string;
|
|
72
|
+
media?: BncrPersistedGroupHistoryMediaEntry[];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type BncrPersistedGroupHistoryMediaEntry = {
|
|
76
|
+
path?: string;
|
|
77
|
+
contentType?: string;
|
|
78
|
+
kind?: 'image' | 'video' | 'audio' | 'document' | 'unknown';
|
|
79
|
+
messageId?: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type BncrPersistedGroupHistoryBucket = {
|
|
83
|
+
key: string;
|
|
84
|
+
entries: BncrPersistedGroupHistoryEntry[];
|
|
85
|
+
};
|
|
86
|
+
|
|
47
87
|
export type BncrStatusRuntimeSnapshot = {
|
|
48
88
|
connected?: boolean;
|
|
49
89
|
running?: boolean;
|
|
@@ -133,6 +173,8 @@ export type PersistedState = {
|
|
|
133
173
|
outbox: OutboxEntry[];
|
|
134
174
|
deadLetter: OutboxEntry[];
|
|
135
175
|
sessionRoutes: BncrPersistedSessionRoute[];
|
|
176
|
+
sceneRegistry?: BncrSceneRecord[];
|
|
177
|
+
groupHistories?: BncrPersistedGroupHistoryBucket[];
|
|
136
178
|
lastSessionByAccount?: BncrPersistedLastSession[];
|
|
137
179
|
lastActivityByAccount?: BncrPersistedAccountTimestamp[];
|
|
138
180
|
lastInboundByAccount?: BncrPersistedAccountTimestamp[];
|
|
@@ -45,34 +45,49 @@ export function createBncrFileInboundInitHandler(runtime: BncrFileInboundRuntime
|
|
|
45
45
|
const chunkSize = runtime.finiteNonNegativeNumberOrNull(params?.chunkSize ?? 256 * 1024);
|
|
46
46
|
const totalChunks = runtime.finiteNonNegativeNumberOrNull(params?.totalChunks);
|
|
47
47
|
const fileSha256 = runtime.asString(params?.fileSha256 || '').trim();
|
|
48
|
+
const routeFromParams = runtime.parseRouteLike({
|
|
49
|
+
platform: runtime.asString(params?.platform || '').trim(),
|
|
50
|
+
groupId: runtime.asString(params?.groupId || '0').trim() || '0',
|
|
51
|
+
userId: runtime.asString(params?.userId || '0').trim() || '0',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const reject = (error: string) => {
|
|
55
|
+
runtime.logWarn(
|
|
56
|
+
'file',
|
|
57
|
+
`file.init reject accountId=${accountId} connId=${connId} clientId=${clientId || '-'} transferId=${transferId || '-'} error=${error} sessionKey=${sessionKey || '-'} fileSize=${fileSize ?? 'null'} chunkSize=${chunkSize ?? 'null'} totalChunks=${totalChunks ?? 'null'}`,
|
|
58
|
+
);
|
|
59
|
+
respond(false, { error });
|
|
60
|
+
};
|
|
48
61
|
|
|
49
62
|
if (!transferId || !sessionKey || !fileSize || !chunkSize || !totalChunks) {
|
|
50
|
-
|
|
63
|
+
reject('transferId/sessionKey/fileSize/chunkSize/totalChunks required');
|
|
51
64
|
return;
|
|
52
65
|
}
|
|
53
66
|
if (fileSize > runtime.inboundFileTransferMaxBytes) {
|
|
54
|
-
|
|
55
|
-
error: `fileSize too large size=${fileSize} max=${runtime.inboundFileTransferMaxBytes}`,
|
|
56
|
-
});
|
|
67
|
+
reject(`fileSize too large size=${fileSize} max=${runtime.inboundFileTransferMaxBytes}`);
|
|
57
68
|
return;
|
|
58
69
|
}
|
|
59
70
|
if (totalChunks > runtime.inboundFileTransferMaxChunks) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
reject(
|
|
72
|
+
`totalChunks too large total=${totalChunks} max=${runtime.inboundFileTransferMaxChunks}`,
|
|
73
|
+
);
|
|
63
74
|
return;
|
|
64
75
|
}
|
|
65
76
|
const expectedTotalChunks = Math.ceil(fileSize / chunkSize);
|
|
66
77
|
if (totalChunks !== expectedTotalChunks) {
|
|
67
|
-
|
|
68
|
-
error: `totalChunks mismatch total=${totalChunks} expected=${expectedTotalChunks}`,
|
|
69
|
-
});
|
|
78
|
+
reject(`totalChunks mismatch total=${totalChunks} expected=${expectedTotalChunks}`);
|
|
70
79
|
return;
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
|
|
82
|
+
let normalized = runtime.normalizeStoredSessionKey(sessionKey);
|
|
83
|
+
if (!normalized && routeFromParams) {
|
|
84
|
+
normalized = {
|
|
85
|
+
sessionKey: runtime.buildCanonicalSessionKey(routeFromParams),
|
|
86
|
+
route: routeFromParams,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
74
89
|
if (!normalized) {
|
|
75
|
-
|
|
90
|
+
reject('invalid sessionKey');
|
|
76
91
|
return;
|
|
77
92
|
}
|
|
78
93
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GatewayRequestHandlerOptions } from 'openclaw/plugin-sdk/core';
|
|
2
|
+
import type { BncrSessionKind } from '../core/targets.ts';
|
|
2
3
|
import type { BncrRoute, FileRecvTransferState } from '../core/types.ts';
|
|
3
4
|
|
|
4
5
|
export type BncrFileInboundLeaseEventKind =
|
|
@@ -40,6 +41,7 @@ export type BncrFileInboundRuntime = {
|
|
|
40
41
|
normalizeStoredSessionKey: (
|
|
41
42
|
sessionKey: string,
|
|
42
43
|
) => { sessionKey: string; route: BncrRoute } | null;
|
|
44
|
+
buildCanonicalSessionKey: (route: BncrRoute, kind?: BncrSessionKind) => string;
|
|
43
45
|
saveInboundMediaBuffer: (args: {
|
|
44
46
|
buffer: Buffer;
|
|
45
47
|
mimeType: string;
|