76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
ForbiddenException,
|
|
Get,
|
|
Headers,
|
|
Inject,
|
|
Param,
|
|
Post,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import { AuthGuard } from '../auth/auth.guard.js';
|
|
import { CurrentUser } from '../auth/current-user.decorator.js';
|
|
import { scopeFromUser, type AuthenticatedUserLike } from '../auth/session-scope.js';
|
|
import type { RuntimeProviderRequestContext } from '../agent/runtime-provider-registry.service.js';
|
|
import type {
|
|
InteractionCoordinationObservationDto,
|
|
InteractionCoordinationResponseDto,
|
|
InteractionCoordinationResultDto,
|
|
CreateHandoffDto,
|
|
} from './interaction-coordination.dto.js';
|
|
import { InteractionCoordinationService } from './interaction-coordination.service.js';
|
|
|
|
/** Authenticated interaction-plane boundary for the handoff/observe/result-only interaction coordination contract. */
|
|
/** `api/coord/interaction` is canonical; the Mos path remains a compatibility alias. */
|
|
@Controller(['api/coord/interaction', 'api/coord/mos'])
|
|
@UseGuards(AuthGuard)
|
|
export class InteractionCoordinationController {
|
|
constructor(
|
|
@Inject(InteractionCoordinationService)
|
|
private readonly coordination: InteractionCoordinationService,
|
|
) {}
|
|
|
|
@Post('handoff')
|
|
async handoff(
|
|
@Body() request: CreateHandoffDto,
|
|
@CurrentUser() user: AuthenticatedUserLike,
|
|
@Headers('x-correlation-id') correlationId?: string,
|
|
): Promise<InteractionCoordinationResponseDto> {
|
|
return { receipt: await this.coordination.handoff(request, this.context(user, correlationId)) };
|
|
}
|
|
|
|
@Get(':handoffId/observe')
|
|
async observe(
|
|
@Param('handoffId') handoffId: string,
|
|
@CurrentUser() user: AuthenticatedUserLike,
|
|
@Headers('x-correlation-id') correlationId?: string,
|
|
): Promise<InteractionCoordinationObservationDto> {
|
|
return {
|
|
observation: await this.coordination.observe(handoffId, this.context(user, correlationId)),
|
|
};
|
|
}
|
|
|
|
@Get(':handoffId/result')
|
|
async result(
|
|
@Param('handoffId') handoffId: string,
|
|
@CurrentUser() user: AuthenticatedUserLike,
|
|
@Headers('x-correlation-id') correlationId?: string,
|
|
): Promise<InteractionCoordinationResultDto> {
|
|
return { result: await this.coordination.result(handoffId, this.context(user, correlationId)) };
|
|
}
|
|
|
|
private context(
|
|
user: AuthenticatedUserLike,
|
|
correlationId?: string,
|
|
): RuntimeProviderRequestContext {
|
|
const requestCorrelationId = correlationId?.trim();
|
|
if (!requestCorrelationId) throw new ForbiddenException('X-Correlation-Id is required');
|
|
return {
|
|
actorScope: scopeFromUser(user),
|
|
channelId: 'cli',
|
|
correlationId: requestCorrelationId,
|
|
};
|
|
}
|
|
}
|