/** * Reply to an existing comment. * * Thin wrapper around add-comment that sets threadId + parentId correctly. * * Usage: * pnpm action reply-to-comment --commentId= --content="..." */ import { defineAction } from "@agent-native/core"; import { writeAppState } from "@agent-native/core/application-state"; import { getRequestUserEmail } from "@agent-native/core/server/request-context"; import { assertAccess } from "@agent-native/core/sharing"; import { eq } from "drizzle-orm"; import { z } from "zod"; import { getDb, schema } from "../server/db/index.js"; import { nanoid } from "../server/lib/recordings.js"; export default defineAction({ description: "Reply to an existing comment. Looks up the thread and parent and delegates to add-comment.", schema: z.object({ commentId: z.string().describe("Comment ID to reply to"), content: z.string().min(1).describe("Reply text"), authorName: z.string().optional(), }), run: async (args) => { const db = getDb(); const [parent] = await db .select() .from(schema.recordingComments) .where(eq(schema.recordingComments.id, args.commentId)) .limit(1); if (!parent) throw new Error(`Comment not found: ${args.commentId}`); await assertAccess("recording", parent.recordingId, "viewer"); const authorEmail = getRequestUserEmail(); if (!authorEmail) { throw new Error("Sign in required to reply to comments."); } const id = nanoid(); const now = new Date().toISOString(); await db.insert(schema.recordingComments).values({ id, recordingId: parent.recordingId, organizationId: parent.organizationId, threadId: parent.threadId, parentId: parent.id, authorEmail, authorName: args.authorName ?? null, content: args.content, videoTimestampMs: parent.videoTimestampMs, createdAt: now, updatedAt: now, }); await writeAppState("refresh-signal", { ts: Date.now() }); console.log( `Replied to comment ${args.commentId} (thread: ${parent.threadId})`, ); return { id, threadId: parent.threadId, parentId: parent.id }; }, });