feat(#709): add configured Discord interaction binding #730

Merged
jason.woltje merged 10 commits from feat/tess-discord into main 2026-07-13 09:02:46 +00:00
Showing only changes of commit 34b82bd257 - Show all commits

View File

@@ -1,4 +1,4 @@
import { Inject, Logger } from '@nestjs/common';
import { Inject, Logger, Optional } from '@nestjs/common';
import {
WebSocketGateway,
WebSocketServer,
@@ -39,6 +39,7 @@ import {
import { BRAIN } from '../brain/brain.tokens.js';
import { CommandRegistryService } from '../commands/command-registry.service.js';
import { CommandExecutorService } from '../commands/command-executor.service.js';
import { CommandAuthorizationService } from '../commands/command-authorization.service.js';
import { RoutingEngineService } from '../agent/routing/routing-engine.service.js';
import { v4 as uuid } from 'uuid';
import { ChatSocketMessageDto } from './chat.dto.js';
@@ -124,6 +125,9 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
@Inject(CommandRegistryService) private readonly commandRegistry: CommandRegistryService,
@Inject(CommandExecutorService) private readonly commandExecutor: CommandExecutorService,
@Inject(RoutingEngineService) private readonly routingEngine: RoutingEngineService,
@Optional()
@Inject(CommandAuthorizationService)
private readonly commandAuthorization: CommandAuthorizationService | null = null,
) {}
afterInit(): void {
@@ -646,19 +650,28 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
): Promise<void> {
if (!client.data.discordService) return;
const ingress = this.resolveDiscordIngress(client, envelope, 'approve');
const command = ingress?.content.match(/^\/approve\s+([a-z][\w-]*)$/i)?.[1];
const actionParts = ingress?.content.match(/^\/approve\s+([^\s]+)\s+([^\s]+)$/i);
const serviceUserId = process.env['DISCORD_SERVICE_USER_ID'];
if (!ingress || !command || !serviceUserId) return;
const approval = await this.commandExecutor.createApproval(
{ command, conversationId: ingress.conversationId },
{
userId: serviceUserId,
tenantId: process.env['DISCORD_SERVICE_TENANT_ID'] ?? serviceUserId,
},
if (!ingress || !actionParts || !serviceUserId || !this.commandAuthorization) return;
const binding = resolveDiscordInteractionBinding(
parseDiscordInteractionBindings(process.env['DISCORD_INTERACTION_BINDINGS']),
ingress.guildId,
ingress.channelId,
ingress.userId,
'approve',
);
if (!binding) return;
const approval = await this.commandAuthorization.createRuntimeTerminationApproval({
providerId: actionParts[1]!,
sessionId: actionParts[2]!,
actorId: serviceUserId,
tenantId: process.env['DISCORD_SERVICE_TENANT_ID'] ?? serviceUserId,
channelId: ingress.channelId,
correlationId: ingress.correlationId,
agentName: binding.instanceId,
});
client.emit('discord:approval', {
correlationId: ingress.correlationId,
command,
success: approval !== null,
approvalId: approval?.approvalId,
expiresAt: approval?.expiresAt,