import { initContract } from '@ts-rest/core'; import { z } from 'zod'; import { errorResponseSchema, insufficientTokensErrorSchema } from '../schemas/common.schemas'; import { tradeAgentInputSchema, tradeAgentResponseSchema } from '../schemas/shopkeeper-trade-agent.schemas'; const c = initContract(); const recordLearnedExampleBodySchema = z.object({ input: z.string().min(1).max(500), intentJson: z.record(z.unknown()), }); const recordRejectedExampleBodySchema = z.object({ input: z.string().min(1).max(500), }); const scheduledTaskSchema = z.object({ id: z.string().uuid(), prompt: z.string(), channel: z.string(), scheduledAt: z.string().datetime(), status: z.string(), createdAt: z.string().datetime(), }); const feedbackBodySchema = z.object({ label: z.enum(['positive', 'negative', 'neutral']), comment: z.string().max(500).optional(), llmLogId: z.string().uuid().optional(), }); export const shopkeeperTradeAgentContract = c.router({ getScheduledTasks: { method: 'GET', path: '/shopkeeper/agent/scheduled-tasks', responses: { 200: z.array(scheduledTaskSchema) }, summary: 'List pending scheduled follow-up tasks for the shop', }, cancelScheduledTask: { method: 'DELETE', path: '/shopkeeper/agent/scheduled-tasks/:id', pathParams: z.object({ id: z.string().uuid() }), body: z.undefined(), responses: { 204: z.undefined(), 404: errorResponseSchema, }, summary: 'Cancel a pending scheduled follow-up task', }, runAgent: { method: 'POST', path: '/shopkeeper/trade/agent', body: tradeAgentInputSchema, responses: { 200: tradeAgentResponseSchema, 400: errorResponseSchema, 401: errorResponseSchema, 402: insufficientTokensErrorSchema, 500: errorResponseSchema, }, summary: 'Run the unified cloud trade agent', }, recordLearnedExample: { method: 'POST', path: '/shopkeeper/agent/learned-example', body: recordLearnedExampleBodySchema, responses: { 204: z.undefined(), 400: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Record a successful (input, intent) from mobile for in-context learning', }, recordFeedback: { method: 'POST', path: '/shopkeeper/trade/agent/feedback', body: feedbackBodySchema, responses: { 204: z.undefined(), 400: errorResponseSchema, 401: errorResponseSchema, 404: errorResponseSchema, }, summary: 'Attach fine-tuning label to LLM request log (latest trade-agent log if llmLogId omitted)', }, recordRejectedExample: { method: 'POST', path: '/shopkeeper/agent/learned-example/rejected', body: recordRejectedExampleBodySchema, responses: { 204: z.undefined(), 400: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Record a rejected parse (e.g. user cancelled ActionPreviewSheet) for future task mining', }, });