diff --git a/packages/api-contracts/src/chats.ts b/packages/api-contracts/src/chats.ts index 7ef769b..4df1bff 100644 --- a/packages/api-contracts/src/chats.ts +++ b/packages/api-contracts/src/chats.ts @@ -1,5 +1,6 @@ import { z } from 'zod'; import { ErrorResponseSchema, RejectedActionResponseSchema } from './common.js'; +import { MAX_CONFIGURED_AUTOMATED_CONVERSATION_TURNS } from './config.js'; import { RedditAccountOwnerSchema } from './session.js'; export const ChatMediaSchema = z.strictObject({ @@ -72,8 +73,8 @@ export const ConversationDetailResponseSchema = ActiveChatSchema.extend({ sentAt: z.number(), }).nullable(), automation: z.strictObject({ - turnsUsed: z.number().int().min(0).max(3), - remainingTurns: z.number().int().min(0).max(3), + turnsUsed: z.number().int().min(0).max(MAX_CONFIGURED_AUTOMATED_CONVERSATION_TURNS), + remainingTurns: z.number().int().min(0).max(MAX_CONFIGURED_AUTOMATED_CONVERSATION_TURNS), status: z.enum(['active', 'image-received', 'human-required', 'boundary', 'user-overridden']), handoffReason: z.string().nullable(), }), diff --git a/packages/api-contracts/src/config.ts b/packages/api-contracts/src/config.ts index 63405c7..4a37cde 100644 --- a/packages/api-contracts/src/config.ts +++ b/packages/api-contracts/src/config.ts @@ -1,5 +1,7 @@ import { z } from 'zod'; +export const MAX_CONFIGURED_AUTOMATED_CONVERSATION_TURNS = 20; + export const ToneKeySchema = z.union([ z.literal('default'), z.literal('dominant'), @@ -27,8 +29,8 @@ export const RuntimeConfigSchema = z.strictObject({ }); export const ConversationBehaviourSchema = z.strictObject({ mode: z.union([z.literal('legacy'), z.literal('r4r'), z.literal('r4r-media'), z.literal('manual')]).default('legacy'), - maxAutomatedTurns: z.number().int().min(1).max(20).default(3), - rapportTurnsBeforeMediaAsk: z.number().int().min(1).max(19).default(3), + maxAutomatedTurns: z.number().int().min(1).max(MAX_CONFIGURED_AUTOMATED_CONVERSATION_TURNS).default(3), + rapportTurnsBeforeMediaAsk: z.number().int().min(1).max(MAX_CONFIGURED_AUTOMATED_CONVERSATION_TURNS - 1).default(3), }).superRefine((behaviour, context) => { if (behaviour.mode === 'r4r-media' && behaviour.rapportTurnsBeforeMediaAsk >= behaviour.maxAutomatedTurns) { context.addIssue({ diff --git a/packages/api-contracts/tests/contracts.test.ts b/packages/api-contracts/tests/contracts.test.ts index cdefaaa..55e3e64 100644 --- a/packages/api-contracts/tests/contracts.test.ts +++ b/packages/api-contracts/tests/contracts.test.ts @@ -184,6 +184,12 @@ describe('dashboard endpoint contracts', () => { automation: { turnsUsed: 1, remainingTurns: 2, status: 'active', handoffReason: null }, syncInProgress: null, }).success).toBe(true); + expect(ConversationDetailResponseSchema.safeParse({ + ...chat, + triggeringPost: null, + automation: { turnsUsed: 4, remainingTurns: 2, status: 'active', handoffReason: null }, + syncInProgress: null, + }).success).toBe(true); expect(ChatsResponseSchema.safeParse([{ ...chat, messages: [{ sender: 'alice' }] }]).success).toBe(false); expect(ChatSyncTriggerResponseSchema.safeParse({ success: true, kind: 'delta' }).success).toBe(true); expect(ChatSyncTriggerResponseSchema.safeParse({ success: false, reason: 'Already running full sync' }).success).toBe(true);