chore(tlon): sync slash commands support from upstream

- Add stripBotMention for proper CommandBody parsing
- Add command authorization logic for owner-only slash commands
- Add CommandAuthorized and CommandSource to context payload
This commit is contained in:
Hunter Miller
2026-02-19 17:32:27 -06:00
committed by Josh Lehman
parent 9e10ae3772
commit 404d1fac36
2 changed files with 40 additions and 1 deletions

View File

@@ -33,6 +33,7 @@ import {
extractCites,
formatModelName,
isBotMentioned,
stripBotMention,
isDmAllowed,
isSummarizationRequest,
type ParsedCite,
@@ -987,6 +988,30 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
? `${senderShip} [${senderRole}] in ${channelNest}`
: `${senderShip} [${senderRole}]`;
// Compute command authorization for slash commands (owner-only)
const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(
messageText,
cfg,
);
let commandAuthorized = false;
if (shouldComputeAuth) {
const useAccessGroups = cfg.commands?.useAccessGroups !== false;
const senderIsOwner = isOwner(senderShip);
commandAuthorized = core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
useAccessGroups,
authorizers: [{ configured: Boolean(effectiveOwnerShip), allowed: senderIsOwner }],
});
// Log when non-owner attempts a slash command (will be silently ignored by Gateway)
if (!commandAuthorized) {
console.log(
`[tlon] Command attempt denied: ${senderShip} is not owner (owner=${effectiveOwnerShip ?? "not configured"})`,
);
}
}
// Prepend attachment annotations to message body (similar to Signal format)
let bodyWithAttachments = messageText;
if (attachments.length > 0) {
@@ -1003,10 +1028,13 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
body: bodyWithAttachments,
});
// Strip bot ship mention for CommandBody so "/status" is recognized as command-only
const commandBody = isGroup ? stripBotMention(messageText, botShipName) : messageText;
const ctxPayload = core.channel.reply.finalizeInboundContext({
Body: body,
RawBody: messageText,
CommandBody: messageText,
CommandBody: commandBody,
From: isGroup ? `tlon:group:${groupChannel}` : `tlon:${senderShip}`,
To: `tlon:${botShipName}`,
SessionKey: route.sessionKey,
@@ -1016,6 +1044,8 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise<v
SenderName: senderShip,
SenderId: senderShip,
SenderRole: senderRole,
CommandAuthorized: commandAuthorized,
CommandSource: "text" as const,
Provider: "tlon",
Surface: "tlon",
MessageSid: messageId,

View File

@@ -125,6 +125,15 @@ export function isBotMentioned(
return false;
}
/**
* Strip bot ship mention from message text for command detection.
* "~bot-ship /status" → "/status"
*/
export function stripBotMention(messageText: string, botShipName: string): string {
if (!messageText || !botShipName) return messageText;
return messageText.replace(normalizeShip(botShipName), "").trim();
}
export function isDmAllowed(senderShip: string, allowlist: string[] | undefined): boolean {
if (!allowlist || allowlist.length === 0) {
return false;