feat(tess): wire durable interaction surfaces
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
Jarvis
2026-07-13 04:28:27 -05:00
parent 84d884b932
commit 451f7e04ec
16 changed files with 782 additions and 58 deletions

View File

@@ -32,7 +32,12 @@ import type {
AbortPayload,
} from '@mosaicstack/types';
import { AgentService, type ConversationHistoryMessage } from '../agent/agent.service.js';
import { RuntimeProviderService } from '../agent/runtime-provider-registry.service.js';
import {
RUNTIME_PROVIDER_AUDIT_SINK,
RuntimeProviderService,
type RuntimeAuditSink,
} from '../agent/runtime-provider-registry.service.js';
import { TessDurableSessionService } from '../agent/tess-durable-session.service.js';
import { AUTH } from '../auth/auth.tokens.js';
import {
scopeFromUser,
@@ -134,6 +139,12 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
@Optional()
@Inject(RuntimeProviderService)
private readonly runtimeRegistry: RuntimeProviderService | null = null,
@Optional()
@Inject(TessDurableSessionService)
private readonly durableSessions: TessDurableSessionService | null = null,
@Optional()
@Inject(RUNTIME_PROVIDER_AUDIT_SINK)
private readonly runtimeAudit: RuntimeAuditSink | null = null,
) {}
afterInit(): void {
@@ -656,9 +667,16 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
): Promise<void> {
if (!client.data.discordService) return;
const ingress = this.resolveDiscordIngress(client, envelope, 'approve');
const actionParts = ingress?.content.match(/^\/approve\s+([^\s]+)\s+([^\s]+)$/i);
const isApprovalCommand = /^\/approve\s*$/i.test(ingress?.content ?? '');
const tenantId = process.env['DISCORD_SERVICE_TENANT_ID']?.trim();
if (!ingress || !actionParts || !tenantId || !this.commandAuthorization) return;
if (
!ingress ||
!isApprovalCommand ||
!tenantId ||
!this.commandAuthorization ||
!this.durableSessions
)
return;
const binding = resolveDiscordInteractionBinding(
parseDiscordInteractionBindings(process.env['DISCORD_INTERACTION_BINDINGS']),
ingress.guildId,
@@ -680,22 +698,63 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
});
return;
}
const providerId = actionParts[1]!;
const sessionId = actionParts[2]!;
let snapshot;
try {
snapshot = await this.durableSessions.getSnapshot(ingress.conversationId, {
actorScope: { userId: actorId, tenantId },
channelId: ingress.channelId,
correlationId: ingress.correlationId,
});
} catch {
client.emit('discord:approval', {
correlationId: ingress.correlationId,
success: false,
approvalId: undefined,
expiresAt: undefined,
});
return;
}
if (snapshot.identity.agentName !== agentName) {
client.emit('discord:approval', {
correlationId: ingress.correlationId,
success: false,
approvalId: undefined,
expiresAt: undefined,
});
return;
}
const approval = await this.commandAuthorization.createRuntimeTerminationApproval({
providerId,
sessionId,
providerId: snapshot.identity.providerId,
sessionId: snapshot.identity.runtimeSessionId,
actorId,
tenantId,
channelId: ingress.channelId,
correlationId: this.discordRuntimeActionCorrelation(
binding.instanceId,
ingress,
providerId,
sessionId,
snapshot.identity.providerId,
snapshot.identity.runtimeSessionId,
),
agentName,
});
if (!approval) {
await this.runtimeAudit?.record({
providerId: snapshot.identity.providerId,
operation: 'session.terminate',
outcome: 'denied',
actorId,
tenantId,
channelId: ingress.channelId,
correlationId: this.discordRuntimeActionCorrelation(
binding.instanceId,
ingress,
snapshot.identity.providerId,
snapshot.identity.runtimeSessionId,
),
resourceId: snapshot.identity.runtimeSessionId,
errorCode: 'policy_denied',
});
}
client.emit('discord:approval', {
correlationId: ingress.correlationId,
success: approval !== null,
@@ -711,9 +770,10 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
): Promise<void> {
if (!client.data.discordService) return;
const ingress = this.resolveDiscordIngress(client, envelope, 'stop');
const actionParts = ingress?.content.match(/^\/stop\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)$/i);
const approvalRef = /^\/stop\s+([^\s]+)$/i.exec(ingress?.content ?? '')?.[1];
const tenantId = process.env['DISCORD_SERVICE_TENANT_ID']?.trim();
if (!ingress || !actionParts || !tenantId || !this.runtimeRegistry) return;
if (!ingress || !approvalRef || !tenantId || !this.runtimeRegistry || !this.durableSessions)
return;
const binding = resolveDiscordInteractionBinding(
parseDiscordInteractionBindings(process.env['DISCORD_INTERACTION_BINDINGS']),
ingress.guildId,
@@ -723,25 +783,31 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
);
const actorId = binding && resolveDiscordInteractionActorId(binding, ingress.userId);
if (!actorId) return;
const providerId = actionParts[1]!;
const sessionId = actionParts[2]!;
try {
const context = {
actorScope: { userId: actorId, tenantId },
channelId: ingress.channelId,
correlationId: ingress.correlationId,
};
const snapshot = await this.durableSessions.getSnapshot(ingress.conversationId, context);
if (snapshot.identity.agentName !== binding.instanceId) throw new Error('agent mismatch');
// RuntimeProviderService consumes the durable approval exactly once using the
// provisioned approving-admin identity, never the Discord service account.
await this.runtimeRegistry.terminate(providerId, sessionId, actionParts[3]!, {
actorScope: {
userId: actorId,
tenantId,
await this.runtimeRegistry.terminate(
snapshot.identity.providerId,
snapshot.identity.runtimeSessionId,
approvalRef,
{
...context,
correlationId: this.discordRuntimeActionCorrelation(
binding.instanceId,
ingress,
snapshot.identity.providerId,
snapshot.identity.runtimeSessionId,
),
},
channelId: ingress.channelId,
correlationId: this.discordRuntimeActionCorrelation(
binding.instanceId,
ingress,
providerId,
sessionId,
),
});
);
client.emit('discord:stop', { correlationId: ingress.correlationId, success: true });
} catch {
client.emit('discord:stop', { correlationId: ingress.correlationId, success: false });