@xmoxmo/bncr 0.6.1 → 0.6.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/README.md +65 -1
- package/package.json +1 -1
- package/scripts/check-pack.mjs +4 -1
- package/scripts/selfcheck.mjs +4 -1
- package/src/channel.ts +144 -5
- package/src/messaging/inbound/commands.ts +62 -26
- package/src/messaging/inbound/context-facts.ts +88 -58
- package/src/messaging/inbound/conversation-history-serial.ts +258 -0
- package/src/messaging/inbound/conversation-history.ts +450 -0
- package/src/messaging/inbound/dispatch.ts +568 -123
- package/src/messaging/inbound/history-shard-worker.ts +180 -0
- package/src/messaging/inbound/native-command.ts +89 -33
- package/src/messaging/inbound/outbound-replay-cache.ts +202 -17
- package/src/messaging/inbound/reply-dispatch.ts +7 -6
- package/src/messaging/inbound/scene-admin.ts +40 -20
- package/src/messaging/inbound/turn-context.ts +101 -37
- package/src/plugin/bridge-lifecycle.ts +8 -0
- package/src/plugin/bridge-outbox-facade.ts +15 -1
- package/src/plugin/channel-components.ts +5 -5
- package/src/plugin/channel-runtime-builders-delivery.ts +2 -2
- package/src/plugin/channel-runtime-constants.ts +2 -0
- package/src/plugin/channel-runtime-types.ts +9 -7
- package/src/plugin/inbound-acceptance.ts +9 -24
- package/src/plugin/inbound-handlers.ts +4 -4
- package/src/plugin/inbound-surface-handlers-group.ts +4 -2
- package/src/plugin/outbox-drain-post-push.ts +3 -0
- package/src/plugin/scene-registry.ts +4 -4
- package/src/plugin/sqlite-state.ts +2109 -0
- package/src/plugin/state-store.ts +731 -69
- package/src/plugin/state-transient-runtime-group.ts +17 -3
- package/src/messaging/inbound/group-history.ts +0 -264
package/README.md
CHANGED
|
@@ -93,9 +93,73 @@ openclaw devices approve --latest
|
|
|
93
93
|
- 入站会话按正式 scene / route 语义接入 OpenClaw,区分私聊与群聊会话。
|
|
94
94
|
- 非管理员私聊支持受控的本地命令能力:`/whoami`、`/status`、`/new`、`/reset`,以及对应的 `/bncr ...` 入口。
|
|
95
95
|
- 非管理员私聊中,不在支持范围内的 slash 命令会由 bncr 直接拒绝,不再 fallback 到 agent。
|
|
96
|
-
-
|
|
96
|
+
- 群聊非管理员执行场景管理命令(`mode` / `allow` / `deny` / `bind` / `revoke` / `list` / `history-*` / `download-media`)时,统一返回 `Admin permission required.`,不再附带用法提示。
|
|
97
|
+
- 群聊非管理员执行 `/bncr new`、`/bncr reset`、`/bncr verbose` 时仍按静默拒绝处理。
|
|
97
98
|
- `/bncr list pending` 与 `/bncr list scenes` 采用按私聊 / 群聊分组的摘要展示格式,便于现场排查 scene 状态。
|
|
98
99
|
|
|
100
|
+
### `/bncr help` 权限过滤
|
|
101
|
+
|
|
102
|
+
`/bncr help` 现在按当前身份动态展示:
|
|
103
|
+
|
|
104
|
+
- 管理员:展示全部命令
|
|
105
|
+
- 私聊非管理员:展示当前可用命令,不展示管理类命令
|
|
106
|
+
- 群聊非管理员:仅展示基础命令,不展示 `mode`、`history-*`、`download-media` 等管理命令
|
|
107
|
+
|
|
108
|
+
示例:
|
|
109
|
+
|
|
110
|
+
管理员:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
🦞 Bncr command usage
|
|
114
|
+
|
|
115
|
+
📌 Bncr builtins
|
|
116
|
+
• /bncr whoami
|
|
117
|
+
• /bncr status
|
|
118
|
+
• /bncr new
|
|
119
|
+
• /bncr reset
|
|
120
|
+
• /bncr verbose on|off|full
|
|
121
|
+
|
|
122
|
+
🛡 Scene approval
|
|
123
|
+
• /bncr allow [<SceneId>]
|
|
124
|
+
• /bncr deny [<SceneId>]
|
|
125
|
+
• /bncr bind <agentId> [<SceneId>]
|
|
126
|
+
• /bncr mode help
|
|
127
|
+
• /bncr mode <admin|mention|hybrid|all|clear> [<SceneId>]
|
|
128
|
+
• /bncr revoke [<SceneId>]
|
|
129
|
+
• /bncr list pending [filters...]
|
|
130
|
+
• /bncr list scenes [filters...]
|
|
131
|
+
|
|
132
|
+
📋 Conversation history
|
|
133
|
+
• /bncr history-help
|
|
134
|
+
• /bncr history-limit [<number>|clear] [<SceneId>]
|
|
135
|
+
• /bncr history-force on|off|clear [<SceneId>]
|
|
136
|
+
|
|
137
|
+
🌐 Remote media
|
|
138
|
+
• /bncr download-media on|off|clear|default on|off [<SceneId>]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
私聊非管理员:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
🦞 Bncr command usage
|
|
145
|
+
|
|
146
|
+
📌 Bncr builtins
|
|
147
|
+
• /bncr whoami
|
|
148
|
+
• /bncr status
|
|
149
|
+
• /bncr new
|
|
150
|
+
• /bncr reset
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
群聊非管理员:
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
🦞 Bncr command usage
|
|
157
|
+
|
|
158
|
+
📌 Bncr builtins
|
|
159
|
+
• /bncr whoami
|
|
160
|
+
• /bncr status
|
|
161
|
+
```
|
|
162
|
+
|
|
99
163
|
---
|
|
100
164
|
|
|
101
165
|
## 5. 架构定位
|
package/package.json
CHANGED
package/scripts/check-pack.mjs
CHANGED
|
@@ -158,6 +158,7 @@ const requiredSourceFiles = [
|
|
|
158
158
|
'src/plugin/setup.ts',
|
|
159
159
|
'src/plugin/state-transient-runtime-group.ts',
|
|
160
160
|
'src/plugin/state-store.ts',
|
|
161
|
+
'src/plugin/sqlite-state.ts',
|
|
161
162
|
'src/plugin/target-runtime.ts',
|
|
162
163
|
'src/plugin/target-status-runtime-group.ts',
|
|
163
164
|
'src/plugin/transient-state-runtime.ts',
|
|
@@ -174,7 +175,9 @@ const requiredSourceFiles = [
|
|
|
174
175
|
'src/bootstrap/register-runtime-singleton.ts',
|
|
175
176
|
'src/core/value-sanitize.ts',
|
|
176
177
|
'src/messaging/inbound/contracts.ts',
|
|
177
|
-
'src/messaging/inbound/
|
|
178
|
+
'src/messaging/inbound/conversation-history.ts',
|
|
179
|
+
'src/messaging/inbound/conversation-history-serial.ts',
|
|
180
|
+
'src/messaging/inbound/history-shard-worker.ts',
|
|
178
181
|
'src/messaging/inbound/native-command-runtime.ts',
|
|
179
182
|
'src/messaging/inbound/reply-dispatch-serial.ts',
|
|
180
183
|
'src/messaging/inbound/scene-admin.ts',
|
package/scripts/selfcheck.mjs
CHANGED
|
@@ -63,7 +63,9 @@ const requiredSourceFiles = [
|
|
|
63
63
|
'src/messaging/inbound/dispatch-prep.ts',
|
|
64
64
|
'src/messaging/inbound/dispatch.ts',
|
|
65
65
|
'src/messaging/inbound/gate.ts',
|
|
66
|
-
'src/messaging/inbound/
|
|
66
|
+
'src/messaging/inbound/conversation-history.ts',
|
|
67
|
+
'src/messaging/inbound/conversation-history-serial.ts',
|
|
68
|
+
'src/messaging/inbound/history-shard-worker.ts',
|
|
67
69
|
'src/messaging/inbound/last-route.ts',
|
|
68
70
|
'src/messaging/inbound/media-url-download.ts',
|
|
69
71
|
'src/messaging/inbound/native-command-runtime.ts',
|
|
@@ -191,6 +193,7 @@ const requiredSourceFiles = [
|
|
|
191
193
|
'src/plugin/scene-registry.ts',
|
|
192
194
|
'src/plugin/setup.ts',
|
|
193
195
|
'src/plugin/state-store.ts',
|
|
196
|
+
'src/plugin/sqlite-state.ts',
|
|
194
197
|
'src/plugin/state-transient-runtime-group.ts',
|
|
195
198
|
'src/plugin/status-runtime.ts',
|
|
196
199
|
'src/plugin/status.ts',
|
package/src/channel.ts
CHANGED
|
@@ -43,9 +43,22 @@ import type {
|
|
|
43
43
|
FileSendTransferState,
|
|
44
44
|
OutboxEntry,
|
|
45
45
|
} from './core/types.ts';
|
|
46
|
-
import
|
|
46
|
+
import {
|
|
47
|
+
type BncrConversationHistoryMap,
|
|
48
|
+
resolveBncrHistoryLimit,
|
|
49
|
+
} from './messaging/inbound/conversation-history.ts';
|
|
50
|
+
import {
|
|
51
|
+
clearConversationHistorySerialLocks,
|
|
52
|
+
readConversationHistorySerialHistoryKeys,
|
|
53
|
+
} from './messaging/inbound/conversation-history-serial.ts';
|
|
54
|
+
import { dispatchBncrOutboundHistoryFlush } from './messaging/inbound/dispatch.ts';
|
|
55
|
+
import {
|
|
56
|
+
type BncrHistoryShardDispatchPayload,
|
|
57
|
+
processBncrHistoryShardSlot,
|
|
58
|
+
} from './messaging/inbound/history-shard-worker.ts';
|
|
47
59
|
import type { BncrOutboundReplayCache } from './messaging/inbound/outbound-replay-cache.ts';
|
|
48
60
|
import type { parseBncrInboundParams } from './messaging/inbound/parse.ts';
|
|
61
|
+
import { runBncrInboundReplyDispatch } from './messaging/inbound/reply-dispatch.ts';
|
|
49
62
|
import { buildEnqueueFromReplyDebugInfo } from './messaging/outbound/diagnostics.ts';
|
|
50
63
|
import { buildBncrMediaOutboundFrame } from './messaging/outbound/media.ts';
|
|
51
64
|
import {
|
|
@@ -126,6 +139,8 @@ import {
|
|
|
126
139
|
FILE_TRANSFER_ACK_TTL_MS,
|
|
127
140
|
FILE_TRANSFER_KEEP_MS,
|
|
128
141
|
FILE_TRANSFER_TERMINAL_KEEP_MS,
|
|
142
|
+
HISTORY_SHARD_WORKER_BATCH_LIMIT,
|
|
143
|
+
HISTORY_SHARD_WORKER_INTERVAL_MS,
|
|
129
144
|
INBOUND_FILE_TRANSFER_MAX_BYTES,
|
|
130
145
|
INBOUND_FILE_TRANSFER_MAX_CHUNKS,
|
|
131
146
|
MAX_ACCOUNT_ACTIVITY_ENTRIES,
|
|
@@ -187,6 +202,7 @@ import {
|
|
|
187
202
|
createBncrRuntimeAckObservabilityBuilder,
|
|
188
203
|
} from './plugin/runtime-diagnostics-snapshot.ts';
|
|
189
204
|
import { BNCR_SETUP_SURFACE } from './plugin/setup.ts';
|
|
205
|
+
import type { BncrHistoryShardRow } from './plugin/sqlite-state.ts';
|
|
190
206
|
import { createBncrStateTransientRuntimeGroup } from './plugin/state-transient-runtime-group.ts';
|
|
191
207
|
import { createBncrTargetStatusRuntimeGroup } from './plugin/target-status-runtime-group.ts';
|
|
192
208
|
import { shouldEmitDedupLog as shouldEmitDedupLogFromRuntime } from './runtime/log-dedupe.ts';
|
|
@@ -293,7 +309,7 @@ class BncrBridgeRuntime {
|
|
|
293
309
|
getSceneRegistry(): Map<string, BncrSceneRecord> {
|
|
294
310
|
return this.sceneRegistry;
|
|
295
311
|
}
|
|
296
|
-
private
|
|
312
|
+
private conversationHistories: BncrConversationHistoryMap = new Map();
|
|
297
313
|
private outboundReplayCache: BncrOutboundReplayCache = new Map();
|
|
298
314
|
private routeAliases = new Map<
|
|
299
315
|
string,
|
|
@@ -340,6 +356,8 @@ class BncrBridgeRuntime {
|
|
|
340
356
|
// Timers / background workers ---------------------------------------------
|
|
341
357
|
private saveTimer: NodeJS.Timeout | null = null;
|
|
342
358
|
private pushTimer: NodeJS.Timeout | null = null;
|
|
359
|
+
private historyShardWorkerTimer: NodeJS.Timeout | null = null;
|
|
360
|
+
private historyShardWorkerRunning = false;
|
|
343
361
|
private pushDrainRunningAccounts = new Set<string>();
|
|
344
362
|
private pushDrainRunningSinceByAccount = new Map<string, number>();
|
|
345
363
|
private pushDrainStuckWarnedAtByAccount = new Map<string, number>();
|
|
@@ -836,6 +854,7 @@ class BncrBridgeRuntime {
|
|
|
836
854
|
initializeCanonicalAgentId: (cfg) => this.initializeCanonicalAgentId(cfg),
|
|
837
855
|
logWarn: (scope, message, options) => this.logWarn(scope, message, options),
|
|
838
856
|
loadState: () => this.loadState(),
|
|
857
|
+
cutoverToSqlite: () => this.stateStore.cutoverToSqlite(),
|
|
839
858
|
setDebugFlag: (value) => {
|
|
840
859
|
BNCR_DEBUG_VERBOSE = value;
|
|
841
860
|
},
|
|
@@ -850,6 +869,7 @@ class BncrBridgeRuntime {
|
|
|
850
869
|
ctx,
|
|
851
870
|
debug,
|
|
852
871
|
);
|
|
872
|
+
this.startHistoryShardWorker();
|
|
853
873
|
};
|
|
854
874
|
|
|
855
875
|
stopService = async () => {
|
|
@@ -867,6 +887,7 @@ class BncrBridgeRuntime {
|
|
|
867
887
|
}
|
|
868
888
|
|
|
869
889
|
private cleanupRuntimeWaitersAndTimers(reason: string) {
|
|
890
|
+
this.stopHistoryShardWorker(reason);
|
|
870
891
|
this.clientRpcRuntime.shutdown();
|
|
871
892
|
cleanupBncrBridgeRuntime(
|
|
872
893
|
{
|
|
@@ -1077,13 +1098,85 @@ class BncrBridgeRuntime {
|
|
|
1077
1098
|
}
|
|
1078
1099
|
|
|
1079
1100
|
private async loadState() {
|
|
1080
|
-
|
|
1101
|
+
// A hot bridge rebuild must not recover shards whose old serial task is
|
|
1102
|
+
// still uploading in this process. Normal process restarts have no active
|
|
1103
|
+
// keys and therefore recover every in-flight shard.
|
|
1104
|
+
const activeHistoryKeys = readConversationHistorySerialHistoryKeys();
|
|
1105
|
+
clearConversationHistorySerialLocks(
|
|
1106
|
+
'runtime-restart',
|
|
1107
|
+
`${this.bridgeId}:${Date.now()}:${Math.random().toString(16).slice(2, 8)}`,
|
|
1108
|
+
);
|
|
1109
|
+
await this.stateStore.recoverInFlightHistoryShards(activeHistoryKeys);
|
|
1110
|
+
await this.stateStore.loadState(activeHistoryKeys);
|
|
1081
1111
|
}
|
|
1082
1112
|
|
|
1083
1113
|
private async flushState() {
|
|
1084
1114
|
await this.stateStore.flushState();
|
|
1085
1115
|
}
|
|
1086
1116
|
|
|
1117
|
+
private startHistoryShardWorker() {
|
|
1118
|
+
if (this.historyShardWorkerTimer) {
|
|
1119
|
+
clearInterval(this.historyShardWorkerTimer);
|
|
1120
|
+
this.historyShardWorkerTimer = null;
|
|
1121
|
+
}
|
|
1122
|
+
if (this.stopped) return;
|
|
1123
|
+
this.historyShardWorkerTimer = setInterval(() => {
|
|
1124
|
+
void this.tickHistoryShardWorker();
|
|
1125
|
+
}, HISTORY_SHARD_WORKER_INTERVAL_MS);
|
|
1126
|
+
this.historyShardWorkerTimer.unref?.();
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
private stopHistoryShardWorker(reason: string) {
|
|
1130
|
+
if (this.historyShardWorkerTimer) {
|
|
1131
|
+
clearInterval(this.historyShardWorkerTimer);
|
|
1132
|
+
this.historyShardWorkerTimer = null;
|
|
1133
|
+
}
|
|
1134
|
+
this.logInfo(
|
|
1135
|
+
'history-shard',
|
|
1136
|
+
`history-shard worker stopped|bridge=${this.bridgeId}|reason=${reason}`,
|
|
1137
|
+
{ debugOnly: true },
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
private async tickHistoryShardWorker() {
|
|
1142
|
+
if (this.historyShardWorkerRunning || this.stopped) return;
|
|
1143
|
+
this.historyShardWorkerRunning = true;
|
|
1144
|
+
try {
|
|
1145
|
+
for (let i = 0; i < HISTORY_SHARD_WORKER_BATCH_LIMIT; i += 1) {
|
|
1146
|
+
const shard = this.stateStore.historyShardQueue.claimNextHistoryShard();
|
|
1147
|
+
if (!shard) break;
|
|
1148
|
+
await this.processHistoryShard(shard);
|
|
1149
|
+
}
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
this.logWarn('inbound', `history shard worker tick failed: ${String(error)}`);
|
|
1152
|
+
} finally {
|
|
1153
|
+
this.historyShardWorkerRunning = false;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
private async processHistoryShard(shard: BncrHistoryShardRow) {
|
|
1158
|
+
const cfg = getOpenClawRuntimeConfig(this.api);
|
|
1159
|
+
await processBncrHistoryShardSlot({
|
|
1160
|
+
shard,
|
|
1161
|
+
historyShardQueue: this.stateStore.historyShardQueue,
|
|
1162
|
+
conversationHistories: this.conversationHistories,
|
|
1163
|
+
outboundReplayCache: this.outboundReplayCache,
|
|
1164
|
+
runDispatch: (payload: BncrHistoryShardDispatchPayload) =>
|
|
1165
|
+
runBncrInboundReplyDispatch({
|
|
1166
|
+
api: this.api,
|
|
1167
|
+
channelId: CHANNEL_ID,
|
|
1168
|
+
cfg,
|
|
1169
|
+
...payload,
|
|
1170
|
+
setInboundActivity: (accountId, at) => {
|
|
1171
|
+
this.lastInboundByAccount.set(accountId, at);
|
|
1172
|
+
this.markActivity(accountId, at);
|
|
1173
|
+
},
|
|
1174
|
+
scheduleSave: () => this.scheduleSave(),
|
|
1175
|
+
enqueueFromReply: (args) => this.enqueueFromReply(args),
|
|
1176
|
+
}),
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1087
1180
|
private resolveMessageAck(messageId: string, result: 'acked' | 'timeout' = 'acked') {
|
|
1088
1181
|
return this.messageAckRuntime.resolveMessageAck(messageId, result);
|
|
1089
1182
|
}
|
|
@@ -1901,7 +1994,7 @@ class BncrBridgeRuntime {
|
|
|
1901
1994
|
maxSessionRouteEntries: MAX_SESSION_ROUTE_ENTRIES,
|
|
1902
1995
|
maxAccountActivityEntries: MAX_ACCOUNT_ACTIVITY_ENTRIES,
|
|
1903
1996
|
sceneRegistry: this.sceneRegistry,
|
|
1904
|
-
|
|
1997
|
+
conversationHistories: this.conversationHistories,
|
|
1905
1998
|
outboundReplayCache: this.outboundReplayCache,
|
|
1906
1999
|
outbox: this.outbox,
|
|
1907
2000
|
getDeadLetter: () => this.deadLetter,
|
|
@@ -2168,6 +2261,21 @@ class BncrBridgeRuntime {
|
|
|
2168
2261
|
deadLetterSinceStartByAccount: this.deadLetterSinceStartByAccount,
|
|
2169
2262
|
lastOutboundByAccount: this.lastOutboundByAccount,
|
|
2170
2263
|
outboundReplayCache: this.outboundReplayCache,
|
|
2264
|
+
conversationHistories: this.conversationHistories,
|
|
2265
|
+
resolveOutboundHistoryLimit: (entry) => {
|
|
2266
|
+
const platform = entry.route?.platform || '';
|
|
2267
|
+
const groupId = entry.route?.groupId || '0';
|
|
2268
|
+
const userId = entry.route?.userId || '0';
|
|
2269
|
+
const sceneKey = groupId !== '0' ? `${platform}:${groupId}` : `${platform}:${userId}`;
|
|
2270
|
+
return resolveBncrHistoryLimit(this.sceneRegistry.get(sceneKey)?.historyLimit);
|
|
2271
|
+
},
|
|
2272
|
+
resolveOutboundHistoryForce: (entry) => {
|
|
2273
|
+
const platform = entry.route?.platform || '';
|
|
2274
|
+
const groupId = entry.route?.groupId || '0';
|
|
2275
|
+
const userId = entry.route?.userId || '0';
|
|
2276
|
+
const sceneKey = groupId !== '0' ? `${platform}:${groupId}` : `${platform}:${userId}`;
|
|
2277
|
+
return this.sceneRegistry.get(sceneKey)?.historyForce !== false;
|
|
2278
|
+
},
|
|
2171
2279
|
resolveOutboundSender: (entry) => {
|
|
2172
2280
|
try {
|
|
2173
2281
|
const account = resolveAccount(getOpenClawRuntimeConfig(this.api), entry.accountId);
|
|
@@ -2176,6 +2284,8 @@ class BncrBridgeRuntime {
|
|
|
2176
2284
|
return { sender: 'OpenClaw', senderId: entry.accountId };
|
|
2177
2285
|
}
|
|
2178
2286
|
},
|
|
2287
|
+
onConversationHistoryOverflow: (entry, historyVersion) =>
|
|
2288
|
+
this.flushConversationHistoryFromOutbound(entry, historyVersion),
|
|
2179
2289
|
isOutboundAckRequired: (accountId) => this.isOutboundAckRequired(accountId),
|
|
2180
2290
|
scheduleSave: () => this.scheduleSave(),
|
|
2181
2291
|
flushPushQueueBestEffort: (args) => this.flushPushQueueBestEffort(args),
|
|
@@ -2763,6 +2873,35 @@ class BncrBridgeRuntime {
|
|
|
2763
2873
|
this.bridgeOutboxFacade.markRecentOutboundAcked(entry);
|
|
2764
2874
|
}
|
|
2765
2875
|
|
|
2876
|
+
private flushConversationHistoryFromOutbound(entry: OutboxEntry, historyVersion?: number) {
|
|
2877
|
+
const cfg = getOpenClawRuntimeConfig(this.api);
|
|
2878
|
+
const canonicalAgentId = this.canonicalAgentId || 'main';
|
|
2879
|
+
void dispatchBncrOutboundHistoryFlush({
|
|
2880
|
+
api: this.api,
|
|
2881
|
+
channelId: CHANNEL_ID,
|
|
2882
|
+
cfg,
|
|
2883
|
+
entry,
|
|
2884
|
+
...(historyVersion !== undefined ? { historyVersion } : {}),
|
|
2885
|
+
canonicalAgentId,
|
|
2886
|
+
sceneRegistry: this.sceneRegistry,
|
|
2887
|
+
conversationHistories: this.conversationHistories,
|
|
2888
|
+
outboundReplayCache: this.outboundReplayCache,
|
|
2889
|
+
defaultAdminAgentId: canonicalAgentId,
|
|
2890
|
+
defaultPublicAgentId: this.defaultPublicAgentId(),
|
|
2891
|
+
now,
|
|
2892
|
+
rememberSessionRoute: (sessionKey, accountId, route) =>
|
|
2893
|
+
this.rememberSessionRoute(sessionKey, accountId, route),
|
|
2894
|
+
enqueueFromReply: (args) => this.enqueueFromReply(args),
|
|
2895
|
+
setInboundActivity: (accountId, at) => {
|
|
2896
|
+
this.lastInboundByAccount.set(accountId, at);
|
|
2897
|
+
this.markActivity(accountId, at);
|
|
2898
|
+
},
|
|
2899
|
+
scheduleSave: () => this.scheduleSave(),
|
|
2900
|
+
}).catch((err) => {
|
|
2901
|
+
this.logWarn('inbound', `outbound history flush failed: ${String(err)}`);
|
|
2902
|
+
});
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2766
2905
|
private logEnqueueFromReply(args: {
|
|
2767
2906
|
accountId: string;
|
|
2768
2907
|
sessionKey: string;
|
|
@@ -2893,7 +3032,7 @@ class BncrBridgeRuntime {
|
|
|
2893
3032
|
defaultAdminAgentId: (args) => this.defaultAdminAgentId(args),
|
|
2894
3033
|
defaultPublicAgentId: () => this.defaultPublicAgentId(),
|
|
2895
3034
|
sceneRegistry: this.sceneRegistry,
|
|
2896
|
-
|
|
3035
|
+
conversationHistories: this.conversationHistories,
|
|
2897
3036
|
outboundReplayCache: this.outboundReplayCache,
|
|
2898
3037
|
prepareInboundAcceptance: (args) => this.prepareInboundAcceptance(args),
|
|
2899
3038
|
logInboundSummary: (args) => this.logInboundSummary(args),
|
|
@@ -224,7 +224,10 @@ export async function handleBncrNativeCommand(params: {
|
|
|
224
224
|
});
|
|
225
225
|
|
|
226
226
|
const nativeVerbose = resolveBncrNativeVerboseCommand(command);
|
|
227
|
-
const nativeHelp = resolveBncrNativeHelpCommand(command
|
|
227
|
+
const nativeHelp = resolveBncrNativeHelpCommand(command, {
|
|
228
|
+
isAdmin: parsed.isAdmin,
|
|
229
|
+
peerKind: parsed.peer.kind as 'direct' | 'group',
|
|
230
|
+
});
|
|
228
231
|
const nativeWhoami = resolveBncrNativeWhoamiCommand({
|
|
229
232
|
command,
|
|
230
233
|
platform: parsed.platform,
|
|
@@ -244,7 +247,10 @@ export async function handleBncrNativeCommand(params: {
|
|
|
244
247
|
resolvedAgentId: resolvedRoute.agentId || canonicalAgentId,
|
|
245
248
|
sessionKey,
|
|
246
249
|
});
|
|
247
|
-
const nativeSessionReset = resolveBncrNativeSessionResetCommand({
|
|
250
|
+
const nativeSessionReset = resolveBncrNativeSessionResetCommand({
|
|
251
|
+
command,
|
|
252
|
+
peerKind: parsed.peer.kind as 'direct' | 'group',
|
|
253
|
+
});
|
|
248
254
|
if (nativeHelp) {
|
|
249
255
|
logBncrNativeCommandSummary(
|
|
250
256
|
buildBncrNativeCommandSummary({
|
|
@@ -336,7 +342,7 @@ export async function handleBncrNativeCommand(params: {
|
|
|
336
342
|
}
|
|
337
343
|
|
|
338
344
|
if (nativeSessionReset) {
|
|
339
|
-
const allowLocalSessionReset = parsed.peer.kind === 'direct';
|
|
345
|
+
const allowLocalSessionReset = parsed.peer.kind === 'direct' || parsed.isAdmin;
|
|
340
346
|
if (!allowLocalSessionReset) {
|
|
341
347
|
logBncrNativeCommandSummary(
|
|
342
348
|
buildBncrNativeCommandSummary({
|
|
@@ -481,8 +487,7 @@ export async function handleBncrNativeCommand(params: {
|
|
|
481
487
|
|
|
482
488
|
const sceneAdmin = parseSceneAdminCommand(command);
|
|
483
489
|
if (sceneAdmin.matched) {
|
|
484
|
-
|
|
485
|
-
if (!sceneAdmin.valid) {
|
|
490
|
+
if (!parsed.isAdmin) {
|
|
486
491
|
logBncrNativeCommandSummary(
|
|
487
492
|
buildBncrNativeCommandSummary({
|
|
488
493
|
kind: 'scene-admin',
|
|
@@ -493,17 +498,50 @@ export async function handleBncrNativeCommand(params: {
|
|
|
493
498
|
result: 'rejected',
|
|
494
499
|
}),
|
|
495
500
|
);
|
|
496
|
-
|
|
497
|
-
|
|
501
|
+
await recordAndPatchBncrInboundSessionEntry({
|
|
502
|
+
storePath,
|
|
503
|
+
sessionKey,
|
|
504
|
+
ctx: ctxPayload,
|
|
505
|
+
patch: sessionIdentityPatch,
|
|
506
|
+
});
|
|
507
|
+
await enqueueFromReply({
|
|
508
|
+
accountId,
|
|
509
|
+
sessionKey,
|
|
510
|
+
route,
|
|
511
|
+
payload: {
|
|
512
|
+
text: 'Admin permission required.',
|
|
513
|
+
replyToId: msgId || undefined,
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
return { handled: true, command: command.command, sessionKey };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (!sceneAdmin.valid) {
|
|
520
|
+
logBncrNativeCommandSummary(
|
|
521
|
+
buildBncrNativeCommandSummary({
|
|
522
|
+
kind: 'scene-admin',
|
|
523
|
+
command: command.command,
|
|
498
524
|
accountId,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
525
|
+
to: displayTo,
|
|
526
|
+
msgId: msgId ?? null,
|
|
527
|
+
result: 'rejected',
|
|
528
|
+
}),
|
|
529
|
+
);
|
|
530
|
+
await recordAndPatchBncrInboundSessionEntry({
|
|
531
|
+
storePath,
|
|
532
|
+
sessionKey,
|
|
533
|
+
ctx: ctxPayload,
|
|
534
|
+
patch: sessionIdentityPatch,
|
|
535
|
+
});
|
|
536
|
+
await enqueueFromReply({
|
|
537
|
+
accountId,
|
|
538
|
+
sessionKey,
|
|
539
|
+
route,
|
|
540
|
+
payload: {
|
|
541
|
+
text: sceneAdmin.text,
|
|
542
|
+
replyToId: msgId || undefined,
|
|
543
|
+
},
|
|
544
|
+
});
|
|
507
545
|
return { handled: true, command: command.command, sessionKey };
|
|
508
546
|
}
|
|
509
547
|
|
|
@@ -531,17 +569,15 @@ export async function handleBncrNativeCommand(params: {
|
|
|
531
569
|
ctx: ctxPayload,
|
|
532
570
|
patch: sessionIdentityPatch,
|
|
533
571
|
});
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
});
|
|
544
|
-
}
|
|
572
|
+
await enqueueFromReply({
|
|
573
|
+
accountId,
|
|
574
|
+
sessionKey,
|
|
575
|
+
route,
|
|
576
|
+
payload: {
|
|
577
|
+
text: outcome.text,
|
|
578
|
+
replyToId: msgId || undefined,
|
|
579
|
+
},
|
|
580
|
+
});
|
|
545
581
|
return { handled: true, command: command.command, sessionKey };
|
|
546
582
|
}
|
|
547
583
|
|