import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { nowIso } from "./_utils/time"; import type { MutationCtx, QueryCtx } from "./_generated/server"; async function assertWorkspaceMember( ctx: QueryCtx | MutationCtx, userId: string, workspaceId: string, ) { const membership = await ctx.db .query("workspace_members") .withIndex("by_workspace_user", (q) => q.eq("workspace_id", workspaceId).eq("user_id", userId), ) .first(); if (!membership) { throw new Error("无权访问该工作空间"); } return membership; } export const getById = query({ args: { userId: v.string(), workspaceId: v.string(), actionId: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); return await ctx.db .query("agent_actions") .withIndex("by_action_id", (q) => q.eq("id", args.actionId)) .first(); }, }); export const listBySession = query({ args: { userId: v.string(), workspaceId: v.string(), sessionId: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); return await ctx.db .query("agent_actions") .withIndex("by_workspace_session", (q) => q.eq("workspace_id", args.workspaceId).eq("session_id", args.sessionId), ) .collect(); }, }); export const createStarted = mutation({ args: { userId: v.string(), workspaceId: v.string(), actionId: v.string(), sessionId: v.string(), actionType: v.string(), actor: v.union(v.literal("human"), v.literal("agent"), v.literal("automation")), input: v.any(), startedAt: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); await ctx.db.insert("agent_actions", { id: args.actionId, workspace_id: args.workspaceId, session_id: args.sessionId, action_type: args.actionType, status: "running", actor: args.actor, input: args.input, output: null, error: null, started_at: args.startedAt, finished_at: null, }); return { ok: true, actionId: args.actionId }; }, }); export const markSucceeded = mutation({ args: { userId: v.string(), workspaceId: v.string(), actionId: v.string(), output: v.any(), finishedAt: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); const row = await ctx.db .query("agent_actions") .withIndex("by_action_id", (q) => q.eq("id", args.actionId)) .first(); if (!row) throw new Error("action log not found"); await ctx.db.patch(row._id, { status: "succeeded", output: args.output, finished_at: args.finishedAt, }); return { ok: true }; }, }); export const markFailed = mutation({ args: { userId: v.string(), workspaceId: v.string(), actionId: v.string(), error: v.string(), finishedAt: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); const row = await ctx.db .query("agent_actions") .withIndex("by_action_id", (q) => q.eq("id", args.actionId)) .first(); if (!row) throw new Error("action log not found"); await ctx.db.patch(row._id, { status: "failed", error: args.error, finished_at: args.finishedAt, }); return { ok: true }; }, });