fix: honor mention-bypass for group commands

This commit is contained in:
Peter Steinberger
2026-01-09 02:52:44 +01:00
parent efa5f0bfe0
commit 3a99ac7e9a
7 changed files with 43 additions and 5 deletions

View File

@@ -56,6 +56,7 @@
- Docs: expand parameter descriptions for agent/wake hooks. (#532) — thanks @mcinteerj
- Docs: add community showcase entries from Discord. (#476) — thanks @gupsammy
- TUI: refresh status bar after think/verbose/reasoning changes. (#519) — thanks @jdrhyne
- Commands: treat mention-bypassed group command messages as mentioned so elevated directives respond.
## 2026.1.8

View File

@@ -15,7 +15,7 @@ read_when:
- **Global availability gate**: `agent.elevated` is global (not per-agent). If disabled or sender not allowlisted, elevated is unavailable everywhere.
- **Per-session state**: `/elevated on|off` sets the elevated level for the current session key.
- **Inline directive**: `/elevated on` inside a message applies to that message only.
- **Groups**: In group chats, elevated directives are only honored when the agent is mentioned.
- **Groups**: In group chats, elevated directives are only honored when the agent is mentioned. Command-only messages that bypass mention requirements are treated as mentioned.
- **Host execution**: elevated runs `bash` on the host (bypasses sandbox).
- **Unsandboxed agents**: when there is no sandbox to bypass, elevated does not change where `bash` runs.
- **Tool policy still applies**: if `bash` is denied by tool policy, elevated cannot be used.

View File

@@ -785,6 +785,7 @@ export function createDiscordMessageHandler(params: {
!hasAnyMention &&
commandAuthorized &&
hasControlCommand(baseText);
const effectiveWasMentioned = wasMentioned || shouldBypassMention;
const canDetectMention = Boolean(botId) || mentionRegexes.length > 0;
if (isGuildMessage && shouldRequireMention) {
if (botId && !wasMentioned && !shouldBypassMention) {
@@ -981,7 +982,7 @@ export function createDiscordMessageHandler(params: {
: undefined,
Provider: "discord" as const,
Surface: "discord" as const,
WasMentioned: wasMentioned,
WasMentioned: effectiveWasMentioned,
MessageSid: message.id,
ParentSessionKey: threadKeys.parentSessionKey,
ThreadStarterBody: threadStarterBody,

View File

@@ -326,6 +326,7 @@ export async function monitorIMessageProvider(
!mentioned &&
commandAuthorized &&
hasControlCommand(messageText);
const effectiveWasMentioned = mentioned || shouldBypassMention;
if (
isGroup &&
requireMention &&
@@ -387,7 +388,7 @@ export async function monitorIMessageProvider(
MediaPath: mediaPath,
MediaType: mediaType,
MediaUrl: mediaPath,
WasMentioned: mentioned,
WasMentioned: effectiveWasMentioned,
CommandAuthorized: commandAuthorized,
// Originating channel for reply routing.
OriginatingChannel: "imessage" as const,

View File

@@ -250,6 +250,39 @@ describe("monitorSlackProvider tool results", () => {
expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true);
});
it("treats control commands as mentions for group bypass", async () => {
replyMock.mockResolvedValue({ text: "ok" });
const controller = new AbortController();
const run = monitorSlackProvider({
botToken: "bot-token",
appToken: "app-token",
abortSignal: controller.signal,
});
await waitForEvent("message");
const handler = getSlackHandlers()?.get("message");
if (!handler) throw new Error("Slack message handler not registered");
await handler({
event: {
type: "message",
user: "U1",
text: "/elevated off",
ts: "123",
channel: "C1",
channel_type: "channel",
},
});
await flush();
controller.abort();
await run;
expect(replyMock).toHaveBeenCalledTimes(1);
expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true);
});
it("threads replies when incoming message is in a thread", async () => {
replyMock.mockResolvedValue({ text: "thread reply" });

View File

@@ -913,6 +913,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
!hasAnyMention &&
commandAuthorized &&
hasControlCommand(message.text ?? "");
const effectiveWasMentioned = wasMentioned || shouldBypassMention;
const canDetectMention = Boolean(botUserId) || mentionRegexes.length > 0;
if (
isRoom &&
@@ -1058,7 +1059,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
ThreadStarterBody: threadStarterBody,
ThreadLabel: threadLabel,
Timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
WasMentioned: isRoomish ? wasMentioned : undefined,
WasMentioned: isRoomish ? effectiveWasMentioned : undefined,
MediaPath: media?.path,
MediaType: media?.contentType,
MediaUrl: media?.path,

View File

@@ -486,6 +486,7 @@ export function createTelegramBot(opts: TelegramBotOptions) {
!hasAnyMention &&
commandAuthorized &&
hasControlCommand(msg.text ?? msg.caption ?? "");
const effectiveWasMentioned = wasMentioned || shouldBypassMention;
const canDetectMention = Boolean(botUsername) || mentionRegexes.length > 0;
if (isGroup && requireMention && canDetectMention) {
if (!wasMentioned && !shouldBypassMention) {
@@ -592,7 +593,7 @@ export function createTelegramBot(opts: TelegramBotOptions) {
ReplyToBody: replyTarget?.body,
ReplyToSender: replyTarget?.sender,
Timestamp: msg.date ? msg.date * 1000 : undefined,
WasMentioned: isGroup ? wasMentioned : undefined,
WasMentioned: isGroup ? effectiveWasMentioned : undefined,
MediaPath: allMedia[0]?.path,
MediaType: allMedia[0]?.contentType,
MediaUrl: allMedia[0]?.path,