From 3f050e5213025ff769b39f5a24eca01bdb5d96c1 Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Sat, 4 Jul 2026 20:31:25 +0200 Subject: [PATCH] refactor(api): introduce mapAgentEnvironmentError oRPC middleware (finding 1) Replace 12 near-identical try/catch blocks in threads/actions/attachments/messages handlers with a single AnyMiddleware that maps the AGENT_ENVIRONMENT_UNAVAILABLE sentinel to PRECONDITION_FAILED ORPCError. Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa --- packages/api/src/features/agent/actions.ts | 10 +-- .../api/src/features/agent/attachments.ts | 30 +++----- packages/api/src/features/agent/messages.ts | 44 +++++------- packages/api/src/features/agent/routing.ts | 11 +++ packages/api/src/features/agent/threads.ts | 68 ++++++------------- 5 files changed, 63 insertions(+), 100 deletions(-) diff --git a/packages/api/src/features/agent/actions.ts b/packages/api/src/features/agent/actions.ts index 383470747..f473a6eb3 100644 --- a/packages/api/src/features/agent/actions.ts +++ b/packages/api/src/features/agent/actions.ts @@ -1,6 +1,6 @@ import z from "zod"; import { protectedProcedure } from "../../context"; -import { isAgentEnvironmentUnavailable, throwUnavailable } from "./routing"; +import { mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; export const actionsRouter = { @@ -13,12 +13,8 @@ export const actionsRouter = { summary: "Restore agent action snapshot", }) .input(z.object({ id: z.string() })) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.actions.revert({ id: input.id, userId: context.user.id }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.actions.revert({ id: input.id, userId: context.user.id }); }), }; diff --git a/packages/api/src/features/agent/attachments.ts b/packages/api/src/features/agent/attachments.ts index 10f5430a0..302b8b632 100644 --- a/packages/api/src/features/agent/attachments.ts +++ b/packages/api/src/features/agent/attachments.ts @@ -1,7 +1,7 @@ import z from "zod"; import { protectedProcedure } from "../../context"; import { storageUploadRateLimit } from "../../middleware/rate-limit"; -import { isAgentEnvironmentUnavailable, throwUnavailable } from "./routing"; +import { mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; function base64ToUint8Array(value: string) { @@ -26,19 +26,15 @@ export const attachmentsRouter = { }), ) .use(storageUploadRateLimit) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.attachments.create({ - userId: context.user.id, - threadId: input.threadId, - filename: input.filename, - mediaType: input.mediaType, - data: base64ToUint8Array(input.data), - }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.attachments.create({ + userId: context.user.id, + threadId: input.threadId, + filename: input.filename, + mediaType: input.mediaType, + data: base64ToUint8Array(input.data), + }); }), delete: protectedProcedure @@ -51,12 +47,8 @@ export const attachmentsRouter = { }) .input(z.object({ id: z.string() })) .output(z.void()) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - await agentService.attachments.delete({ id: input.id, userId: context.user.id }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + await agentService.attachments.delete({ id: input.id, userId: context.user.id }); }), }; diff --git a/packages/api/src/features/agent/messages.ts b/packages/api/src/features/agent/messages.ts index af395716c..89c982556 100644 --- a/packages/api/src/features/agent/messages.ts +++ b/packages/api/src/features/agent/messages.ts @@ -2,7 +2,7 @@ import type { UIMessage } from "ai"; import z from "zod"; import { protectedProcedure } from "../../context"; import { aiRequestRateLimit } from "../../middleware/rate-limit"; -import { isAgentEnvironmentUnavailable, isUiMessage, throwUnavailable } from "./routing"; +import { isUiMessage, mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; export const messagesRouter = { @@ -22,18 +22,14 @@ export const messagesRouter = { }), ) .use(aiRequestRateLimit) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.messages.send({ - userId: context.user.id, - threadId: input.threadId, - message: input.message, - ...(input.attachmentIds ? { attachmentIds: input.attachmentIds } : {}), - }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.messages.send({ + userId: context.user.id, + threadId: input.threadId, + message: input.message, + ...(input.attachmentIds ? { attachmentIds: input.attachmentIds } : {}), + }); }), stop: protectedProcedure @@ -51,17 +47,13 @@ export const messagesRouter = { }), ) .output(z.void()) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - await agentService.messages.stop({ - userId: context.user.id, - threadId: input.threadId, - ...(input.partialMessage ? { partialMessage: input.partialMessage } : {}), - }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + await agentService.messages.stop({ + userId: context.user.id, + threadId: input.threadId, + ...(input.partialMessage ? { partialMessage: input.partialMessage } : {}), + }); }), resume: protectedProcedure @@ -73,12 +65,8 @@ export const messagesRouter = { summary: "Resume agent message stream", }) .input(z.object({ threadId: z.string() })) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.messages.resume({ userId: context.user.id, threadId: input.threadId }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.messages.resume({ userId: context.user.id, threadId: input.threadId }); }), }; diff --git a/packages/api/src/features/agent/routing.ts b/packages/api/src/features/agent/routing.ts index d7c7a48e3..10324cd62 100644 --- a/packages/api/src/features/agent/routing.ts +++ b/packages/api/src/features/agent/routing.ts @@ -1,3 +1,4 @@ +import type { AnyMiddleware } from "@orpc/server"; import type { UIMessage } from "ai"; import { ORPCError } from "@orpc/client"; @@ -21,3 +22,13 @@ export function isUiMessage(value: unknown): value is UIMessage { Array.isArray(message.parts) ); } + +// ponytail: single middleware replaces 12 near-identical try/catch blocks across agent route handlers +export const mapAgentEnvironmentError: AnyMiddleware = async ({ next }) => { + try { + return await next(); + } catch (error) { + if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); + throw error; + } +}; diff --git a/packages/api/src/features/agent/threads.ts b/packages/api/src/features/agent/threads.ts index 32baf7f43..9a3371c0a 100644 --- a/packages/api/src/features/agent/threads.ts +++ b/packages/api/src/features/agent/threads.ts @@ -1,6 +1,6 @@ import z from "zod"; import { protectedProcedure } from "../../context"; -import { isAgentEnvironmentUnavailable, throwUnavailable } from "./routing"; +import { mapAgentEnvironmentError } from "./routing"; import { agentService } from "./service"; export const threadsRouter = { @@ -12,13 +12,9 @@ export const threadsRouter = { operationId: "listAgentThreads", summary: "List agent threads", }) + .use(mapAgentEnvironmentError) .handler(async ({ context }) => { - try { - return await agentService.threads.list({ userId: context.user.id }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.threads.list({ userId: context.user.id }); }), create: protectedProcedure @@ -30,18 +26,14 @@ export const threadsRouter = { summary: "Create agent thread", }) .input(z.object({ aiProviderId: z.string().optional(), sourceResumeId: z.string().optional() })) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.threads.create({ - userId: context.user.id, - locale: context.locale, - ...(input.aiProviderId ? { aiProviderId: input.aiProviderId } : {}), - ...(input.sourceResumeId ? { sourceResumeId: input.sourceResumeId } : {}), - }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.threads.create({ + userId: context.user.id, + locale: context.locale, + ...(input.aiProviderId ? { aiProviderId: input.aiProviderId } : {}), + ...(input.sourceResumeId ? { sourceResumeId: input.sourceResumeId } : {}), + }); }), getOrCreateForResume: protectedProcedure @@ -53,17 +45,13 @@ export const threadsRouter = { summary: "Get or create an in-resume agent thread", }) .input(z.object({ resumeId: z.string(), aiProviderId: z.string().optional() })) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.threads.getOrCreateForResume({ - userId: context.user.id, - resumeId: input.resumeId, - ...(input.aiProviderId ? { aiProviderId: input.aiProviderId } : {}), - }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.threads.getOrCreateForResume({ + userId: context.user.id, + resumeId: input.resumeId, + ...(input.aiProviderId ? { aiProviderId: input.aiProviderId } : {}), + }); }), get: protectedProcedure @@ -75,13 +63,9 @@ export const threadsRouter = { summary: "Get agent thread", }) .input(z.object({ id: z.string() })) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - return await agentService.threads.get({ id: input.id, userId: context.user.id }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + return await agentService.threads.get({ id: input.id, userId: context.user.id }); }), archive: protectedProcedure @@ -94,13 +78,9 @@ export const threadsRouter = { }) .input(z.object({ id: z.string() })) .output(z.void()) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - await agentService.threads.archive({ id: input.id, userId: context.user.id }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + await agentService.threads.archive({ id: input.id, userId: context.user.id }); }), delete: protectedProcedure @@ -113,12 +93,8 @@ export const threadsRouter = { }) .input(z.object({ id: z.string() })) .output(z.void()) + .use(mapAgentEnvironmentError) .handler(async ({ context, input }) => { - try { - await agentService.threads.delete({ id: input.id, userId: context.user.id }); - } catch (error) { - if (isAgentEnvironmentUnavailable(error)) throwUnavailable(); - throw error; - } + await agentService.threads.delete({ id: input.id, userId: context.user.id }); }), };