Files
spoon/packages/backend/convex/spoonAgentSettings.ts
T
Gabriel Brown 2dfa97ee4f
Build and Push Next App / quality (push) Failing after 48s
Build and Push Next App / build-next (push) Has been skipped
Add agent workflows & stuff
2026-06-21 21:15:15 -05:00

119 lines
3.6 KiB
TypeScript

import { v } from 'convex/values';
import type { Doc } from './_generated/dataModel';
import { mutation, query } from './_generated/server';
import { getOwnedSpoon, getRequiredUserId, optionalText } from './model';
const reasoningEffort = v.union(
v.literal('none'),
v.literal('minimal'),
v.literal('low'),
v.literal('medium'),
v.literal('high'),
v.literal('xhigh'),
);
const defaults = {
enabled: true,
branchPrefix: 'spoon/agent',
agentModel: 'gpt-5.1-codex',
reasoningEffort: 'high' as const,
maxJobDurationMs: 1_800_000,
maxOutputBytes: 200_000,
};
export const getForSpoon = query({
args: { spoonId: v.id('spoons') },
handler: async (ctx, { spoonId }) => {
const ownerId = await getRequiredUserId(ctx);
const spoon = await getOwnedSpoon(ctx, spoonId, ownerId);
const settings = await ctx.db
.query('spoonAgentSettings')
.withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
.first();
return (
settings ?? {
spoonId,
ownerId,
...defaults,
defaultBaseBranch:
spoon.forkDefaultBranch ?? spoon.upstreamDefaultBranch,
}
);
},
});
export const update = mutation({
args: {
spoonId: v.id('spoons'),
enabled: v.optional(v.boolean()),
defaultBaseBranch: v.optional(v.string()),
branchPrefix: v.optional(v.string()),
installCommand: v.optional(v.string()),
checkCommand: v.optional(v.string()),
testCommand: v.optional(v.string()),
agentModel: v.optional(v.string()),
reasoningEffort: v.optional(reasoningEffort),
maxJobDurationMs: v.optional(v.number()),
maxOutputBytes: v.optional(v.number()),
},
handler: async (ctx, args) => {
const ownerId = await getRequiredUserId(ctx);
const spoon = await getOwnedSpoon(ctx, args.spoonId, ownerId);
const now = Date.now();
let settings = await ctx.db
.query('spoonAgentSettings')
.withIndex('by_spoon', (q) => q.eq('spoonId', args.spoonId))
.first();
if (!settings) {
const id = await ctx.db.insert('spoonAgentSettings', {
spoonId: args.spoonId,
ownerId,
...defaults,
defaultBaseBranch:
spoon.forkDefaultBranch ?? spoon.upstreamDefaultBranch,
createdAt: now,
updatedAt: now,
});
settings = await ctx.db.get(id);
}
if (!settings) throw new Error('Agent settings not found.');
const patch: Partial<Doc<'spoonAgentSettings'>> = { updatedAt: now };
if (args.enabled !== undefined) patch.enabled = args.enabled;
if (args.defaultBaseBranch !== undefined) {
patch.defaultBaseBranch = optionalText(args.defaultBaseBranch);
}
if (args.branchPrefix !== undefined) {
patch.branchPrefix =
optionalText(args.branchPrefix) ?? defaults.branchPrefix;
}
if (args.installCommand !== undefined) {
patch.installCommand = optionalText(args.installCommand);
}
if (args.checkCommand !== undefined) {
patch.checkCommand = optionalText(args.checkCommand);
}
if (args.testCommand !== undefined) {
patch.testCommand = optionalText(args.testCommand);
}
if (args.agentModel !== undefined) {
patch.agentModel = optionalText(args.agentModel) ?? defaults.agentModel;
}
if (args.reasoningEffort !== undefined) {
patch.reasoningEffort = args.reasoningEffort;
}
if (args.maxJobDurationMs !== undefined) {
patch.maxJobDurationMs = Math.max(60_000, args.maxJobDurationMs);
}
if (args.maxOutputBytes !== undefined) {
patch.maxOutputBytes = Math.max(10_000, args.maxOutputBytes);
}
await ctx.db.patch(settings._id, patch);
return { success: true };
},
});