diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 86e12248959..491014a3cdb 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -670,6 +670,7 @@ export { requestConfirmationResultSchema, createIssueThreadInteractionSchema, acceptIssueThreadInteractionSchema, + cancelIssueThreadInteractionSchema, rejectIssueThreadInteractionSchema, respondIssueThreadInteractionSchema, linkIssueApprovalSchema, @@ -704,6 +705,7 @@ export { type AddIssueComment, type CreateIssueThreadInteraction, type AcceptIssueThreadInteraction, + type CancelIssueThreadInteraction, type RejectIssueThreadInteraction, type RespondIssueThreadInteraction, type LinkIssueApproval, diff --git a/packages/shared/src/validators/index.ts b/packages/shared/src/validators/index.ts index e9141631466..8a1621f3e95 100644 --- a/packages/shared/src/validators/index.ts +++ b/packages/shared/src/validators/index.ts @@ -176,6 +176,7 @@ export { requestConfirmationResultSchema, createIssueThreadInteractionSchema, acceptIssueThreadInteractionSchema, + cancelIssueThreadInteractionSchema, rejectIssueThreadInteractionSchema, respondIssueThreadInteractionSchema, linkIssueApprovalSchema, @@ -193,6 +194,7 @@ export { type AddIssueComment, type CreateIssueThreadInteraction, type AcceptIssueThreadInteraction, + type CancelIssueThreadInteraction, type RejectIssueThreadInteraction, type RespondIssueThreadInteraction, type LinkIssueApproval, diff --git a/packages/shared/src/validators/issue.ts b/packages/shared/src/validators/issue.ts index 9cf3ffddb3a..882b3dd8dc4 100644 --- a/packages/shared/src/validators/issue.ts +++ b/packages/shared/src/validators/issue.ts @@ -448,6 +448,11 @@ export const rejectIssueThreadInteractionSchema = z.object({ }); export type RejectIssueThreadInteraction = z.infer; +export const cancelIssueThreadInteractionSchema = z.object({ + reason: z.string().trim().max(4000).optional(), +}); +export type CancelIssueThreadInteraction = z.infer; + export const respondIssueThreadInteractionSchema = z.object({ answers: z.array(askUserQuestionsAnswerSchema).max(20), summaryMarkdown: multilineTextSchema.pipe(z.string().max(20000)).nullable().optional(), diff --git a/server/src/routes/issues.ts b/server/src/routes/issues.ts index 3b3cce9938a..60916144bf6 100644 --- a/server/src/routes/issues.ts +++ b/server/src/routes/issues.ts @@ -7,6 +7,7 @@ import { issueExecutionDecisions } from "@paperclipai/db"; import { addIssueCommentSchema, acceptIssueThreadInteractionSchema, + cancelIssueThreadInteractionSchema, createIssueAttachmentMetadataSchema, createIssueThreadInteractionSchema, createIssueWorkProductSchema, @@ -3184,7 +3185,7 @@ export function issueRoutes( router.post( "/issues/:id/interactions/:interactionId/cancel", - validate(rejectIssueThreadInteractionSchema), + validate(cancelIssueThreadInteractionSchema), async (req, res) => { const id = req.params.id as string; const interactionId = req.params.interactionId as string; diff --git a/server/src/services/issue-thread-interactions.ts b/server/src/services/issue-thread-interactions.ts index db1f31b531a..de6e165487e 100644 --- a/server/src/services/issue-thread-interactions.ts +++ b/server/src/services/issue-thread-interactions.ts @@ -13,6 +13,7 @@ import type { AcceptIssueThreadInteraction, AskUserQuestionsAnswer, AskUserQuestionsInteraction, + CancelIssueThreadInteraction, CreateIssueThreadInteraction, IssueThreadInteraction, RequestConfirmationInteraction, @@ -26,6 +27,7 @@ import { acceptIssueThreadInteractionSchema, askUserQuestionsPayloadSchema, askUserQuestionsResultSchema, + cancelIssueThreadInteractionSchema, createIssueThreadInteractionSchema, rejectIssueThreadInteractionSchema, requestConfirmationPayloadSchema, @@ -1152,10 +1154,10 @@ export function issueThreadInteractionService(db: Db) { cancelQuestions: async ( issue: { id: string; companyId: string }, interactionId: string, - input: RejectIssueThreadInteraction, + input: CancelIssueThreadInteraction, actor: InteractionActor, ) => { - const data = rejectIssueThreadInteractionSchema.parse(input); + const data = cancelIssueThreadInteractionSchema.parse(input); const current = await db .select() .from(issueThreadInteractions) diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index 1f0ffdfd5a6..f66ad83f683 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -1025,7 +1025,7 @@ function IssueDetailActivityTab({ {hasIssueTreeCost && issueTreeCostSummary ? (
- Including sub-issues ${(issueTreeCostSummary.costCents / 100).toLocaleString(undefined, { + Including sub-issues {(issueTreeCostSummary.costCents / 100).toLocaleString(undefined, { style: "currency", currency: "USD", minimumFractionDigits: 4, @@ -1688,11 +1688,11 @@ export function IssueDetail() { }, }); const pauseIssueWorkRun = useMutation({ - mutationFn: async (runId: string) => { + mutationFn: async ({ runId, scope }: { runId: string; scope: "leaf" | "subtree" }) => { const created = await issuesApi.createTreeHold(issueId!, { mode: "pause", reason: "Paused from active run controls.", - releasePolicy: { strategy: "manual", note: treeControlScope === "leaf" ? "leaf_pause" : "full_pause" }, + releasePolicy: { strategy: "manual", note: scope === "leaf" ? "leaf_pause" : "full_pause" }, metadata: { source: "issue_active_run_control", runId }, }); return created; @@ -3654,11 +3654,11 @@ export function IssueDetail() { onAttachImage={handleCommentAttachImage} onInterruptQueued={handleInterruptQueuedRun} onPauseWorkRun={canManageTreeControl - ? (runId) => pauseIssueWorkRun.mutateAsync(runId).then(() => undefined) + ? (runId) => pauseIssueWorkRun.mutateAsync({ runId, scope: treeControlScope }).then(() => undefined) : undefined} onCancelQueued={handleCancelQueuedComment} interruptingQueuedRunId={interruptQueuedComment.isPending ? interruptQueuedComment.variables ?? null : null} - pausingWorkRunId={pauseIssueWorkRun.isPending ? pauseIssueWorkRun.variables ?? null : null} + pausingWorkRunId={pauseIssueWorkRun.isPending ? pauseIssueWorkRun.variables?.runId ?? null : null} onImageClick={handleChatImageClick} onAcceptInteraction={handleAcceptInteraction} onRejectInteraction={handleRejectInteraction}