import { Router } from 'express'; import { z } from 'zod'; import type { RuntimeStoryActionRequest } from '../../../../packages/shared/src/contracts/story.js'; import type { AppContext } from '../../context.js'; import { badRequest } from '../../errors.js'; import { asyncHandler, sendApiResponse } from '../../http.js'; import { requireJwtAuth } from '../../middleware/auth.js'; import { routeMeta } from '../../middleware/routeMeta.js'; import { getRuntimeStoryState, resolveRuntimeStoryAction, } from './storyActionService.js'; const actionPayloadSchema = z.record(z.string(), z.unknown()); const runtimeStoryActionSchema = z.object({ sessionId: z.string().trim().min(1), clientVersion: z.number().int().min(0).optional(), action: z.object({ type: z.literal('story_choice'), functionId: z.string().trim().min(1), targetId: z.string().trim().optional(), payload: actionPayloadSchema.optional().default({}), }), }); export function createStoryActionRoutes(context: AppContext) { const router = Router(); const requireAuth = requireJwtAuth(context.config, context.userRepository); router.use(requireAuth); router.post( '/actions/resolve', routeMeta({ operation: 'runtime.story.actions.resolve' }), asyncHandler(async (request, response) => { const payload = runtimeStoryActionSchema.parse( request.body, ) as RuntimeStoryActionRequest; sendApiResponse( response, await resolveRuntimeStoryAction({ runtimeRepository: context.runtimeRepository, llmClient: context.llmClient, userId: request.userId!, request: payload, }), ); }), ); router.get( '/state/:sessionId', routeMeta({ operation: 'runtime.story.state.get' }), asyncHandler(async (request, response) => { const sessionId = request.params.sessionId?.trim() || ''; if (!sessionId) { throw badRequest('sessionId is required'); } sendApiResponse( response, await getRuntimeStoryState({ runtimeRepository: context.runtimeRepository, userId: request.userId!, sessionId, }), ); }), ); return router; }