@xmoxmo/bncr 0.6.2 → 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 +3 -0
- package/scripts/selfcheck.mjs +3 -0
- package/src/channel.ts +134 -7
- package/src/messaging/inbound/commands.ts +62 -26
- package/src/messaging/inbound/conversation-history-serial.ts +258 -0
- package/src/messaging/inbound/conversation-history.ts +174 -55
- package/src/messaging/inbound/dispatch.ts +557 -137
- 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 +128 -19
- package/src/messaging/inbound/reply-dispatch.ts +7 -6
- package/src/messaging/inbound/scene-admin.ts +17 -12
- package/src/plugin/bridge-lifecycle.ts +8 -0
- package/src/plugin/bridge-outbox-facade.ts +6 -1
- package/src/plugin/channel-runtime-constants.ts +2 -0
- package/src/plugin/outbox-drain-post-push.ts +3 -0
- package/src/plugin/sqlite-state.ts +2109 -0
- package/src/plugin/state-store.ts +704 -46
- package/src/plugin/state-transient-runtime-group.ts +14 -0
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',
|
|
@@ -175,6 +176,8 @@ const requiredSourceFiles = [
|
|
|
175
176
|
'src/core/value-sanitize.ts',
|
|
176
177
|
'src/messaging/inbound/contracts.ts',
|
|
177
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
|
@@ -64,6 +64,8 @@ const requiredSourceFiles = [
|
|
|
64
64
|
'src/messaging/inbound/dispatch.ts',
|
|
65
65
|
'src/messaging/inbound/gate.ts',
|
|
66
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
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
OpenClawPluginApi,
|
|
5
5
|
OpenClawPluginServiceContext,
|
|
6
6
|
} from 'openclaw/plugin-sdk/core';
|
|
7
|
-
import { DEFAULT_GROUP_HISTORY_LIMIT as DEFAULT_HISTORY_LIMIT } from 'openclaw/plugin-sdk/reply-history';
|
|
8
7
|
import {
|
|
9
8
|
BNCR_DEFAULT_ACCOUNT_ID,
|
|
10
9
|
CHANNEL_ID,
|
|
@@ -44,9 +43,22 @@ import type {
|
|
|
44
43
|
FileSendTransferState,
|
|
45
44
|
OutboxEntry,
|
|
46
45
|
} from './core/types.ts';
|
|
47
|
-
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';
|
|
48
59
|
import type { BncrOutboundReplayCache } from './messaging/inbound/outbound-replay-cache.ts';
|
|
49
60
|
import type { parseBncrInboundParams } from './messaging/inbound/parse.ts';
|
|
61
|
+
import { runBncrInboundReplyDispatch } from './messaging/inbound/reply-dispatch.ts';
|
|
50
62
|
import { buildEnqueueFromReplyDebugInfo } from './messaging/outbound/diagnostics.ts';
|
|
51
63
|
import { buildBncrMediaOutboundFrame } from './messaging/outbound/media.ts';
|
|
52
64
|
import {
|
|
@@ -127,6 +139,8 @@ import {
|
|
|
127
139
|
FILE_TRANSFER_ACK_TTL_MS,
|
|
128
140
|
FILE_TRANSFER_KEEP_MS,
|
|
129
141
|
FILE_TRANSFER_TERMINAL_KEEP_MS,
|
|
142
|
+
HISTORY_SHARD_WORKER_BATCH_LIMIT,
|
|
143
|
+
HISTORY_SHARD_WORKER_INTERVAL_MS,
|
|
130
144
|
INBOUND_FILE_TRANSFER_MAX_BYTES,
|
|
131
145
|
INBOUND_FILE_TRANSFER_MAX_CHUNKS,
|
|
132
146
|
MAX_ACCOUNT_ACTIVITY_ENTRIES,
|
|
@@ -188,6 +202,7 @@ import {
|
|
|
188
202
|
createBncrRuntimeAckObservabilityBuilder,
|
|
189
203
|
} from './plugin/runtime-diagnostics-snapshot.ts';
|
|
190
204
|
import { BNCR_SETUP_SURFACE } from './plugin/setup.ts';
|
|
205
|
+
import type { BncrHistoryShardRow } from './plugin/sqlite-state.ts';
|
|
191
206
|
import { createBncrStateTransientRuntimeGroup } from './plugin/state-transient-runtime-group.ts';
|
|
192
207
|
import { createBncrTargetStatusRuntimeGroup } from './plugin/target-status-runtime-group.ts';
|
|
193
208
|
import { shouldEmitDedupLog as shouldEmitDedupLogFromRuntime } from './runtime/log-dedupe.ts';
|
|
@@ -341,6 +356,8 @@ class BncrBridgeRuntime {
|
|
|
341
356
|
// Timers / background workers ---------------------------------------------
|
|
342
357
|
private saveTimer: NodeJS.Timeout | null = null;
|
|
343
358
|
private pushTimer: NodeJS.Timeout | null = null;
|
|
359
|
+
private historyShardWorkerTimer: NodeJS.Timeout | null = null;
|
|
360
|
+
private historyShardWorkerRunning = false;
|
|
344
361
|
private pushDrainRunningAccounts = new Set<string>();
|
|
345
362
|
private pushDrainRunningSinceByAccount = new Map<string, number>();
|
|
346
363
|
private pushDrainStuckWarnedAtByAccount = new Map<string, number>();
|
|
@@ -837,6 +854,7 @@ class BncrBridgeRuntime {
|
|
|
837
854
|
initializeCanonicalAgentId: (cfg) => this.initializeCanonicalAgentId(cfg),
|
|
838
855
|
logWarn: (scope, message, options) => this.logWarn(scope, message, options),
|
|
839
856
|
loadState: () => this.loadState(),
|
|
857
|
+
cutoverToSqlite: () => this.stateStore.cutoverToSqlite(),
|
|
840
858
|
setDebugFlag: (value) => {
|
|
841
859
|
BNCR_DEBUG_VERBOSE = value;
|
|
842
860
|
},
|
|
@@ -851,6 +869,7 @@ class BncrBridgeRuntime {
|
|
|
851
869
|
ctx,
|
|
852
870
|
debug,
|
|
853
871
|
);
|
|
872
|
+
this.startHistoryShardWorker();
|
|
854
873
|
};
|
|
855
874
|
|
|
856
875
|
stopService = async () => {
|
|
@@ -868,6 +887,7 @@ class BncrBridgeRuntime {
|
|
|
868
887
|
}
|
|
869
888
|
|
|
870
889
|
private cleanupRuntimeWaitersAndTimers(reason: string) {
|
|
890
|
+
this.stopHistoryShardWorker(reason);
|
|
871
891
|
this.clientRpcRuntime.shutdown();
|
|
872
892
|
cleanupBncrBridgeRuntime(
|
|
873
893
|
{
|
|
@@ -1078,13 +1098,85 @@ class BncrBridgeRuntime {
|
|
|
1078
1098
|
}
|
|
1079
1099
|
|
|
1080
1100
|
private async loadState() {
|
|
1081
|
-
|
|
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);
|
|
1082
1111
|
}
|
|
1083
1112
|
|
|
1084
1113
|
private async flushState() {
|
|
1085
1114
|
await this.stateStore.flushState();
|
|
1086
1115
|
}
|
|
1087
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
|
+
|
|
1088
1180
|
private resolveMessageAck(messageId: string, result: 'acked' | 'timeout' = 'acked') {
|
|
1089
1181
|
return this.messageAckRuntime.resolveMessageAck(messageId, result);
|
|
1090
1182
|
}
|
|
@@ -2175,10 +2267,14 @@ class BncrBridgeRuntime {
|
|
|
2175
2267
|
const groupId = entry.route?.groupId || '0';
|
|
2176
2268
|
const userId = entry.route?.userId || '0';
|
|
2177
2269
|
const sceneKey = groupId !== '0' ? `${platform}:${groupId}` : `${platform}:${userId}`;
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
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;
|
|
2182
2278
|
},
|
|
2183
2279
|
resolveOutboundSender: (entry) => {
|
|
2184
2280
|
try {
|
|
@@ -2188,6 +2284,8 @@ class BncrBridgeRuntime {
|
|
|
2188
2284
|
return { sender: 'OpenClaw', senderId: entry.accountId };
|
|
2189
2285
|
}
|
|
2190
2286
|
},
|
|
2287
|
+
onConversationHistoryOverflow: (entry, historyVersion) =>
|
|
2288
|
+
this.flushConversationHistoryFromOutbound(entry, historyVersion),
|
|
2191
2289
|
isOutboundAckRequired: (accountId) => this.isOutboundAckRequired(accountId),
|
|
2192
2290
|
scheduleSave: () => this.scheduleSave(),
|
|
2193
2291
|
flushPushQueueBestEffort: (args) => this.flushPushQueueBestEffort(args),
|
|
@@ -2775,6 +2873,35 @@ class BncrBridgeRuntime {
|
|
|
2775
2873
|
this.bridgeOutboxFacade.markRecentOutboundAcked(entry);
|
|
2776
2874
|
}
|
|
2777
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
|
+
|
|
2778
2905
|
private logEnqueueFromReply(args: {
|
|
2779
2906
|
accountId: string;
|
|
2780
2907
|
sessionKey: string;
|
|
@@ -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
|
|